diff --git a/src/blockRenames.ts b/src/blockRenames.ts new file mode 100644 index 00000000..81f2d29b --- /dev/null +++ b/src/blockRenames.ts @@ -0,0 +1,103 @@ + +// postflatenning +const blockItemRenames = { + "1.13.2": { + "blocks": [], + "items": [ + ] + }, + "1.14": { + "blocks": [], + "items": [ + ["sign", "oak_sign"], + ["rose_red", "red_dye"], + ["cactus_green", "green_dye"], + ["dandelion_yellow", "yellow_dye"] + ] + }, + "1.14.4": { + "blocks": [ + ["sign", "oak_sign"], + ["wall_sign", "oak_wall_sign"] + ], + "items": [ + ] + }, + "1.15.2": { + "blocks": [], + "items": [] + }, + "1.16.1": { + "blocks": [], + "items": [ + ["zombie_pigman_spawn_egg", "zombified_piglin_spawn_egg"] + ] + }, + "1.16.2": { + "blocks": [], + "items": [ + ] + }, + "1.17": { + "blocks": [ + ["grass_path", "dirt_path"], + ], + "items": [ + ["grass_path", "dirt_path"], + ] + }, + "1.18": { + "blocks": [], + "items": [ + ] + }, + "1.19": { + "blocks": [], + "items": [] + }, + "1.19.3": { + "blocks": [], + "items": [] + }, + "1.19.4": { + "blocks": [], + "items": [] + }, + "1.20": { + "blocks": [], + "items": [ + ["pottery_shard_archer", "archer_pottery_sherd"], + ["pottery_shard_prize", "prize_pottery_sherd"], + ["pottery_shard_arms_up", "arms_up_pottery_sherd"], + ["pottery_shard_skull", "skull_pottery_sherd"] + ] + } +} + +const versionToNumber = (ver: string) => { + const [x, y = '0', z = '0'] = ver.split('.') + return +`${x.padStart(2, '0')}${y.padStart(2, '0')}${z.padStart(2, '0')}` +} + +const allRenamesMapFromLatest = Object.fromEntries( + ['blocks', 'items'].map(x => + [ + x, + Object.fromEntries(Object.entries(blockItemRenames).flatMap(([ver, t]) => t[x].map(([oldName, newName]) => [ + newName, + { version: versionToNumber(ver), oldName } + ]))) + ]) +) as { [thing: string]: Record } + +export const adoptBlockOrItemNamesFromLatest = (type: 'blocks' | 'items', version: string, names: string[]) => { + const map = allRenamesMapFromLatest[type] + const ver = versionToNumber(version) + return names.map(name => { + const renamed = map[name] // todo it might be useful if followed by chain + if (renamed && ver < renamed.version) { + return renamed.oldName + } + return name + }) +} diff --git a/src/globals.d.ts b/src/globals.d.ts index 29d4846f..86984c38 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -11,3 +11,11 @@ interface NodeRequire { // webpack bundling context: (path: string, deep: boolean, filter: RegExp) => { keys: () => string[]; (id: string): any } } + +interface ObjectConstructor { + keys (obj: T): Array> + entries (obj: T): Array<[StringKeys, T[keyof T]]> + // todo review https://stackoverflow.com/questions/57390305/trying-to-get-fromentries-type-right + fromEntries> (obj: T): Record + assign, K extends Record> (target: T, source: K): asserts target is T & K +} diff --git a/src/lib/command.js b/src/lib/command.ts similarity index 80% rename from src/lib/command.js rename to src/lib/command.ts index c20f5b45..f4f631c2 100644 --- a/src/lib/command.js +++ b/src/lib/command.ts @@ -1,10 +1,33 @@ -//@ts-check import UserError from './user_error' +type Ctx

= P extends true ? { + player: Player +} : { + player?: Player +} + +type NonFalsey = T extends false ? never : NonNullable + +type AddParams = { + base: string, + aliases?: string[], + info: string, + usage: string, + onlyPlayer?: P + op?: boolean + parse?: (string: string, ctx: Ctx

) => T + action: (data: NonFalsey, ctx: Ctx

) => any + tab?: string[] +} + class Command { - constructor (params, parent, hash) { - this.params = params - this.parent = parent + hash: any + uniqueHash: any + parentBase: any + base: any + path: string + + constructor (public params, public parent?, hash?) { this.hash = parent ? parent.hash : {} this.uniqueHash = parent ? parent.uniqueHash : {} this.parentBase = (this.parent && this.parent.base && this.parent.base + ' ') || '' @@ -21,7 +44,7 @@ class Command { return undefined } - async use (command, ctx = {}, op = true) { + async use (command, ctx: Ctx = {}, op = true) { const resultsFound = this.find(command) let parsedResponse if (resultsFound) { @@ -70,11 +93,11 @@ class Command { this.uniqueHash[this.base] = this } - add (params) { + add (params: AddParams) { return new Command(params, this) } - space (end) { + space (end = false) { const first = !(this.parent && this.parent.parent) return this.params.merged || (!end && first) ? '' : ' ' } diff --git a/src/lib/modules/blocks.ts b/src/lib/modules/blocks.ts index e36bc3f3..9e2c735c 100644 --- a/src/lib/modules/blocks.ts +++ b/src/lib/modules/blocks.ts @@ -72,18 +72,18 @@ export const server = function (serv: Server, { version }: Options) { }, action (params, ctx) { let res = params.slice(1, 4) - if (ctx.player) res = res.map((val, i) => serv.posFromString(val, ctx.player.position[['x', 'y', 'z'][i]])) + 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 blockParam = params[4] const id = isNaN(+blockParam) ? mcData.blocksByName[skipMcPrefix(blockParam)]?.id : +blockParam - const data = parseInt(params[5] || 0, 10) + const data = parseInt(params[5] || '0', 10) const stateId = postFlatenning ? data ? (blocks[id].minStateId! + data) : blocks[id].defaultState! : (id << 4 | data) - if (ctx.player) ctx.player.setBlock(new Vec3(res[0], res[1], res[2]).floored(), stateId) - else serv.setBlock(serv.overworld, new Vec3(res[0], res[1], res[2]).floored(), stateId) + if (ctx.player) ctx.player.setBlock(new Vec3(+res[0], +res[1], +res[2]).floored(), stateId) + else serv.setBlock(serv.overworld, new Vec3(+res[0], +res[1], +res[2]).floored(), stateId) } }) @@ -98,8 +98,8 @@ export const server = function (serv: Server, { version }: Options) { return results }, action (params, ctx) { - if (ctx.player) ctx.player.setBlockAction(new Vec3(params[1], params[2], params[3]).floored(), params[4], params[5]) - else serv.setBlockAction(serv.overworld, new Vec3(params[1], params[2], params[3]).floored(), params[4], params[5]) + if (ctx.player) ctx.player.setBlockAction(new Vec3(+params[1], +params[2], +params[3]).floored(), +params[4], params[5]) + else serv.setBlockAction(serv.overworld, new Vec3(+params[1], +params[2], +params[3]).floored(), +params[4], params[5]) } }) } diff --git a/src/lib/modules/commands.ts b/src/lib/modules/commands.ts index 210cdc82..8213f75b 100644 --- a/src/lib/modules/commands.ts +++ b/src/lib/modules/commands.ts @@ -1,5 +1,6 @@ import colors from 'colors' import UserError from '../user_error' +import { Vec3 } from 'vec3' export const player = function (player: Player, serv: Server, { version }: Options) { player.handleCommand = async (str) => { @@ -12,6 +13,7 @@ export const player = function (player: Player, serv: Server, { version }: Optio } } } +function changeType(arg): asserts arg is T { } export const entity = function (entity: Entity, serv: Server) { entity.selectorString = (str) => serv.selectorString(str, entity.position, entity.world) @@ -32,7 +34,7 @@ export const server = function (serv: Server, { version }: Options) { base: 'ping', info: 'to pong!', usage: '/ping [number]', - action (params, ctx) { + action(params, ctx) { const num = params[0] * 1 + 1 let str = 'pong' @@ -48,9 +50,9 @@ export const server = function (serv: Server, { version }: Options) { info: 'for modpe commands', usage: '/modpe ', onlyPlayer: true, - parse (str) { return str || false }, - action (str, ctx) { - ctx.player.emit('modpe', str) + parse(str) { return str || false }, + action(str, ctx) { + ctx.player!.emit('modpe', str) } }) @@ -58,7 +60,7 @@ export const server = function (serv: Server, { version }: Options) { base: 'version', info: 'to get version of the server', usage: '/version', - action () { + action() { return 'This server is running flying-squid version ' + version } }) @@ -67,7 +69,7 @@ export const server = function (serv: Server, { version }: Options) { base: 'bug', info: 'to bug report', usage: '/bug', - action () { + action() { return 'Report bugs or issues here: https://github.com/PrismarineJS/flying-squid/issues' } }) @@ -77,10 +79,10 @@ export const server = function (serv: Server, { version }: Options) { info: 'Get entities id from selector like @a', usage: '/selector ', op: true, - parse (str) { + parse(str) { return str || false }, - action (sel, ctx) { + action(sel, ctx) { const arr = ctx.player ? serv.selectorString(sel, ctx.player.position, ctx.player.world) : serv.selectorString(sel) if (ctx.player) ctx.player.chat(JSON.stringify(arr.map(a => a.id))) else serv.info(JSON.stringify(arr.map(a => a.id))) @@ -93,7 +95,7 @@ export const server = function (serv: Server, { version }: Options) { info: 'to show all commands', usage: '/help [command]', tab: ['command'], - parse (str) { + parse(str) { const params = str.split(' ') const page = parseInt(params[params.length - 1]) if (page) { @@ -102,7 +104,7 @@ export const server = function (serv: Server, { version }: Options) { const search = params.join(' ') return { search, page: (page && page - 1) || 0 } }, - action ({ search, page }, ctx) { + action({ search, page }, ctx) { if (page < 0) return 'Page # must be >= 1' const hash = serv.commands.uniqueHash @@ -119,7 +121,7 @@ export const server = function (serv: Server, { version }: Options) { if (ctx.player) ctx.player.chat(usage + ': ' + info) else serv.info(usage + ': ' + info) } else { // Multiple commands found, give list with pages - const totalPages = Math.ceil((found.length - 1) / PAGE_LENGTH).toString() + const totalPages = Math.ceil((found.length - 1) / PAGE_LENGTH) if (page >= totalPages) return 'There are only ' + totalPages + ' help pages' found = found.sort() if (found.indexOf('search') !== -1) { @@ -128,7 +130,7 @@ export const server = function (serv: Server, { version }: Options) { else serv.info(baseCmd.base + ' -' + ((baseCmd.params && baseCmd.params.info && ' ' + baseCmd.params.info) || '=-=-=-=-=-=-=-=-')) } else { if (ctx.player) ctx.player.chat('&2--=[ &fHelp&2, page &f' + (page + 1) + ' &2of &f' + totalPages + ' &2]=--') - else serv.info(colors.green('--=[ ') + colors.white('Help') + colors.green(', page ') + colors.white(page + 1) + colors.green(' of ') + colors.white(totalPages) + colors.green(' ]=--')) + else serv.info(colors.green('--=[ ') + colors.white('Help') + colors.green(', page ') + colors.white(String(page + 1)) + colors.green(' of ') + colors.white(String(totalPages)) + colors.green(' ]=--')) } for (let i = PAGE_LENGTH * page; i < Math.min(PAGE_LENGTH * (page + 1), found.length); i++) { if (found[i] === search) continue @@ -147,7 +149,7 @@ export const server = function (serv: Server, { version }: Options) { info: 'Stop the server', usage: '/stop', op: true, - action () { + action() { serv.quit('Server closed') process.exit() } @@ -158,10 +160,10 @@ export const server = function (serv: Server, { version }: Options) { info: 'Broadcast a message', usage: '/say ', op: true, - parse (params) { + parse(params) { return params || false }, - action (params, ctx) { + action(params, ctx) { const who = ctx.player ? ctx.player.username : 'Server' serv.broadcast(`[${who}] ` + params) serv.info(`[CHAT]: [${who}] ` + params) @@ -173,17 +175,17 @@ export const server = function (serv: Server, { version }: Options) { info: 'Displays a message about yourself', usage: '/me ', op: false, - parse (params) { + parse(params) { return params || false }, - action (params, ctx) { + action(params, ctx) { const who = ctx.player ? ctx.player.username : 'Server' serv.broadcast(`* ${who} ` + params) serv.info(`* ${who} ` + params) } }) - function shuffleArray (array) { + function shuffleArray(array) { let currentIndex = array.length let temporaryValue let randomIndex @@ -213,7 +215,7 @@ export const server = function (serv: Server, { version }: Options) { : (type === 'all' || type === 'entity' ? Object.keys(serv.entities).length : 1) const pos = opt.pos - let sample + let sample!: Entity[] if (type === 'all') sample = serv.players else if (type === 'self') sample = serv.players.filter(p => p.id === selfEntityId) else if (type === 'random' || type === 'near') sample = serv.players.filter(p => p.health !== 0) @@ -251,6 +253,7 @@ export const server = function (serv: Server, { version }: Options) { }) sample = sample.filter(s => { + changeType(s) // TODO implement team & scores if ((notudf(opt.radius) && s.position.distanceTo(pos) > opt.radius) || (notudf(opt.minRadius) && s.position.distanceTo(pos) < opt.minRadius) || (notudf(opt.gameMode) && s.gameMode !== opt.gameMode) || @@ -280,7 +283,7 @@ export const server = function (serv: Server, { version }: Options) { return !fail }) - if (type === 'near') sample.sort((a, b) => a.position.distanceTo(opt.pos) > b.position.distanceTo(opt.pos)) + if (type === 'near') sample.sort((a, b) => a.position.distanceTo(opt.pos) - b.position.distanceTo(opt.pos)) else if (type === 'random') sample = shuffleArray(sample) else sample = sample.reverse() // Front = newest @@ -294,8 +297,8 @@ export const server = function (serv: Server, { version }: Options) { 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 find nearest players') if (match === null) throw new UserError('Invalid selector format') + if (match[1] === 'r' && !pos) throw new UserError('Can\'t find nearest players') const typeConversion = { a: 'all', r: 'random', @@ -339,7 +342,8 @@ export const server = function (serv: Server, { version }: Options) { } optPair.forEach(({ key, val }) => { - if (['x', 'y', 'z'].indexOf(key) !== -1) pos[key] = val + // todo + if (['x', 'y', 'z'].indexOf(key) !== -1 && pos) pos[key] = val else if (!optConversion[key]) { data[key] = val } else { @@ -360,15 +364,15 @@ export const server = function (serv: Server, { version }: Options) { } declare global { interface Player { - "handleCommand": (str: any) => Promise + "handleCommand": (str: string) => Promise } interface Entity { - "selectorString": (str: any) => any + "selectorString": (str: string) => Entity[] } interface Server { - "handleCommand": (str: any) => Promise - "selector": (type: any, opt: any, selfEntityId: any) => any - "selectorString": (str: any, pos?: any, world?: any, allowUser?: boolean | undefined, ctxEntityId?: any) => any - "posFromString": (str: any, pos: any) => any + "handleCommand": (str: string) => Promise + "selector": (type: any, opt: any, selfEntityId: any) => Entity[] + "selectorString": (str: string, pos?: Vec3, world?: any, allowUser?: boolean | undefined, ctxEntityId?: any) => Entity[] + "posFromString": (str: string, pos: number) => any } } diff --git a/src/lib/modules/index.ts b/src/lib/modules/index.ts index 6ff54a52..722e635c 100644 --- a/src/lib/modules/index.ts +++ b/src/lib/modules/index.ts @@ -10,12 +10,12 @@ export const initPlugins = () => { if (process.platform === 'browser') { const isWebpack = !!require.context if (isWebpack) { - const pluginsMap = require.context('./', false, /^(?!.*(?:external.js$)).*\.js$/) - module.exports.builtinPlugins = filterKeys(pluginsMap.keys()).map(k => pluginsMap(k)) + const pluginsMap = require.context('./', false, /^(?!.*(?:external.[jt]s$)).*\.js$/) + builtinPlugins = filterKeys(pluginsMap.keys()).map(k => pluginsMap(k)) } else { // esbuild custom plugin - const files = require(/* webpackIgnore: true */ 'esbuild-import-glob(path:.,skipFiles:index.js,external.js)') - module.exports.builtinPlugins = Object.values(files) + const files = require(/* webpackIgnore: true */ 'esbuild-import-glob(path:.,skipFiles:index.js,external.js,index.ts,external.ts)') + builtinPlugins = Object.values(files) } } else { // todo use browser field or bundle like valtio does: https://github.com/webpack/webpack/issues/8826#issuecomment-671402668 @@ -23,6 +23,6 @@ export const initPlugins = () => { const path = require('path') const _plugins = requireIndex(path.join(__dirname, './')) - module.exports.builtinPlugins = filterKeys(Object.keys(_plugins)).map(k => _plugins[k]) + builtinPlugins = filterKeys(Object.keys(_plugins)).map(k => _plugins[k]) } } diff --git a/src/lib/modules/moderation.ts b/src/lib/modules/moderation.ts index 8d834177..de43fda8 100644 --- a/src/lib/modules/moderation.ts +++ b/src/lib/modules/moderation.ts @@ -20,7 +20,7 @@ export const server = function (serv: Server, settings: Options) { } Object.keys(serv.players) .filter(uuid => serv.players[uuid]._client.socket?.remoteAddress === IP) - .forEach(uuid => serv.players[uuid].kick(serv.bannedIPs[serv.players[uuid]._client.socket?.remoteAddress].reason)) + .forEach(uuid => serv.players[uuid].kick(serv.bannedIPs[serv.players[uuid]._client.socket?.remoteAddress!].reason)) return true } else return false } @@ -88,7 +88,7 @@ export const server = function (serv: Server, settings: Options) { // get player, by non-case-sensitive username const player = serv.players.find(player => player.username.toLowerCase() === inputUsername.toLowerCase()) if (player === undefined || player === null) { - const arr = serv.selectorString(params) + const arr = serv.selectorString(params) as Player[] if (arr.length === 0) throw new UserError('Could not find player') arr.map(entity => { @@ -115,13 +115,12 @@ export const server = function (serv: Server, settings: Options) { op: true, parse (params) { if (!params.match(/([a-zA-Z0-9_]+)/)) return false - return params + return params.split(' ') }, action (params) { - params = params.split(' ') const player = serv.getPlayer(params[0]) if (player === undefined || player === null) { - const arr = serv.selectorString(params) + const arr = serv.selectorString(params[0]) as Player[] if (arr.length === 0) throw new UserError('Could not find player') arr.map(entity => { @@ -291,7 +290,7 @@ export const server = function (serv: Server, settings: Options) { if (ctx.player) { ctx.player.chat(`There are ${pllist.length} total banned players${pllist.length > 0 ? ':' : ''}`) pllist.forEach(e => { - ctx.player.chat(e) + ctx.player!.chat(e) }) } else { serv.info(`There are ${pllist.length} total banned players${pllist.length > 0 ? ':' : ''}`) @@ -303,7 +302,7 @@ export const server = function (serv: Server, settings: Options) { if (ctx.player) { ctx.player.chat(`There are ${iplist.length} total banned IP addresses${iplist.length > 0 ? ':' : ''}`) iplist.forEach(e => { - ctx.player.chat(e) + ctx.player!.chat(e) }) } else { serv.info(`There are ${iplist.length} total banned IP addresses${iplist.length > 0 ? ':' : ''}`) diff --git a/src/lib/modules/players.ts b/src/lib/modules/players.ts index d75fb484..7142cc56 100644 --- a/src/lib/modules/players.ts +++ b/src/lib/modules/players.ts @@ -105,8 +105,8 @@ export const server = function (serv: Server, { version }: Options) { usage: '/give [count]', tab: ['player', 'item', 'number'], op: true, - parse (args, ctx) { - args = args.split(' ') + parse (_args, ctx) { + const args = _args.split(' ') if (args[0] === '') return false const players = serv.getPlayers(args[0], ctx.player) if (players.length < 1) throw new UserError('Player not found') @@ -114,7 +114,7 @@ export const server = function (serv: Server, { version }: Options) { return { players, item: skipMcPrefix(args[1]), - count: args[2] ? args[2] : 1 + count: args[2] ? +args[2] : 1 } }, action ({ players, item, count }) { @@ -128,7 +128,7 @@ export const server = function (serv: Server, { version }: Options) { for (const slot of player.inventory.slots) { if (!slot) continue if (slot.type === newItem.type) { - slot.count += parseInt(count) + slot.count += count player.inventory.updateSlot(slot.slot, slot) slotToUpdateFound = true break @@ -148,8 +148,8 @@ export const server = function (serv: Server, { version }: Options) { usage: '/enchant [level]', tab: ['selector', 'item_enchantment', 'number'], op: true, - parse (args, ctx) { - args = args.split(' ') + parse (_args, ctx) { + const args = _args.split(' ') if (args[0] === '') return false const enchantment = mcData.enchantmentsByName[skipMcPrefix(args[1])] if (!enchantment) throw new UserError('No such enchantment') diff --git a/src/lib/modules/portal.ts b/src/lib/modules/portal.ts index 8c860c22..ed166cd3 100644 --- a/src/lib/modules/portal.ts +++ b/src/lib/modules/portal.ts @@ -79,12 +79,12 @@ export const server = function (serv: Server, { version }: Options) { parse (str, ctx) { const pars = str.split(' ') if (pars.length !== 6) { return false } - let [x, y, z, direction, width, height] = pars; + let [x, y, z, directionStr, width, height] = pars; [x, y, z] = [x, y, z].map((val, i) => serv.posFromString(val, ctx.player.position[['x', 'y', 'z'][i]])) - const bottomLeft = new Vec3(x, y, z) - if (direction !== 'x' && direction !== 'z') { throw new UserError('Wrong Direction') } - direction = direction === 'x' ? new Vec3(1, 0, 0) : new Vec3(0, 0, 1) - return { bottomLeft, direction, width, height } + const bottomLeft = new Vec3(+x, +y, +z) + if (directionStr !== 'x' && directionStr !== 'z') { throw new UserError('Wrong Direction') } + const direction = directionStr === 'x' ? new Vec3(1, 0, 0) : new Vec3(0, 0, 1) + return { bottomLeft, direction, width: +width, height: +height } }, async action ({ bottomLeft, direction, width, height }, ctx) { if (width > 21 || height > 21) { throw new UserError('Portals can only be 21x21!') } diff --git a/src/lib/modules/pvp.ts b/src/lib/modules/pvp.ts index 7dcd0b05..aae0ab09 100644 --- a/src/lib/modules/pvp.ts +++ b/src/lib/modules/pvp.ts @@ -79,7 +79,7 @@ export const server = function (serv: Server) { if (arr.length === 0) throw new UserError('Could not find player') arr.forEach(entity => { entity.kill() - serv.info(`Killed ${colors.bold(entity.type === 'player' ? entity.username : entity.name)}`) + serv.info(`Killed ${colors.bold(entity.type === 'player' ? (entity as Player).username : entity.name ?? '')}`) }) } } else { diff --git a/src/lib/modules/spawn.ts b/src/lib/modules/spawn.ts index e525de28..0bfebd60 100644 --- a/src/lib/modules/spawn.ts +++ b/src/lib/modules/spawn.ts @@ -141,7 +141,7 @@ export const server = function (serv: Server, options: Options) { parse (str) { const args = str.split(' ') if (args.length !== 2) { return false } - return { number: args[0], name: args[1] } + return { number: +args[0], name: args[1] } }, action ({ number, name }, ctx) { if (Object.keys(serv.entities).length > options['max-entities'] - number) { throw new UserError('Too many mobs !') } diff --git a/src/lib/modules/tp.ts b/src/lib/modules/tp.ts index b60d8c00..4da1e40a 100644 --- a/src/lib/modules/tp.ts +++ b/src/lib/modules/tp.ts @@ -1,50 +1,49 @@ import { Vec3 } from 'vec3' import UserError from '../user_error' -export const server = (serv) => { +export const server = (serv: Server) => { serv.commands.add({ base: 'teleport', aliases: ['tp'], info: 'to teleport a player', usage: '/teleport [target player] [y] [z]', - onlyPlayer: true, // only for now op: true, parse (str) { return str.match(/^(((.* )?~?-?\d* ~?-?\d* ~?-?\d*)|(.+ .+))$/) ? str.split(' ') : false }, action (args, ctx) { + // todo use position of command block + const selectorString = ctx.player ? ctx.player.selectorString : serv.selectorString if (args.length === 2) { - const entitiesFrom = ctx.player.selectorString(args[0]) - let entityTo = ctx.player.selectorString(args[1]) - if (entityTo.length === 0) throw new UserError('Invalid target') - entityTo = entityTo[0] + const entitiesFrom = selectorString(args[0]) + const entityTo = selectorString(args[1])[0] + if (!entityTo) throw new UserError('Invalid target') entitiesFrom.forEach(e => e.teleport(entityTo.position)) - } else if (args.length === 3) { - let x = serv.posFromString(args[0], ctx.player.position.x) - let y = serv.posFromString(args[1], ctx.player.position.y) - let z = serv.posFromString(args[2], ctx.player.position.z) + } else if (args.length === 3 || args.length === 4) { + if (args.length === 3 && !ctx.player) throw new UserError('Only player can execute command with 3 arguments') + const entitiesFrom = args.length === 3 ? [ctx.player!] : selectorString(args[0]) + const posArgs = args.length === 3 ? args : args.slice(1) + for (const e of entitiesFrom) { + let x = serv.posFromString(posArgs[0], e.position.x) + let y = serv.posFromString(posArgs[1], e.position.y) + let z = serv.posFromString(posArgs[2], e.position.z) + x = Math.floor(x) + y = Math.floor(y) + z = Math.floor(z) - if (Math.abs(x) > 29999999 || Math.abs(y) > 4096 || Math.abs(z) > 29999999) { - // Vanilla Minecraft limits - throw new UserError('Invalid position') - } - - // Vanilla behavior: teleport to center of block if decimal not specified + if (Math.abs(x) > 29_999_999 || Math.abs(y) > 4096 || Math.abs(z) > 29_999_999) { + // Vanilla Minecraft limits + throw new UserError('Invalid position') + } - if (args[0].indexOf('.') === -1) x += 0.5 - if (args[1].indexOf('.') === -1) y += 0.5 - if (args[2].indexOf('.') === -1) z += 0.5 + // Vanilla behavior: teleport to center of block if decimal not specified - ctx.player.teleport(new Vec3(x, y, z)) - } else if (args.length === 4) { - const entitiesFrom = ctx.player.selectorString(args[0]) - - entitiesFrom.forEach(e => e.teleport(new Vec3( - serv.posFromString(args[1], e.position.x), - serv.posFromString(args[2], e.position.y), - serv.posFromString(args[3], e.position.z) - ))) + if (args[0].indexOf('.') === -1) x += 0.5 + if (args[1].indexOf('.') === -1) y += 0.5 + if (args[2].indexOf('.') === -1) z += 0.5 + e.teleport(new Vec3(x, y, z)) + } } } }) diff --git a/src/lib/modules/updatePositions.ts b/src/lib/modules/updatePositions.ts index 8c75a749..554b925b 100644 --- a/src/lib/modules/updatePositions.ts +++ b/src/lib/modules/updatePositions.ts @@ -168,7 +168,7 @@ declare global { pitch: number onGround: boolean "sendSelfPosition": () => void - "sendPosition": (position: Vec3, onGround: any, teleport?: boolean) => any - "teleport": (pos: any) => void + "sendPosition": (position: Vec3, onGround: boolean, teleport?: boolean) => any + "teleport": (pos: Vec3) => void } } diff --git a/tsconfig.json b/tsconfig.json index 03509843..8d0040ac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "sourceMap": true, "target": "ESNext", "module": "CommonJS", + "declaration": true, "esModuleInterop": true, "skipLibCheck": true },