From 7ea64d1e6aa19eb9d66ee0c11133b0502be9f33c Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Wed, 20 May 2026 17:08:03 -0700 Subject: [PATCH] feat: rework commands and fix bugs --- .gitignore | 3 +- package-lock.json | 36 +++++++ package.json | 5 + packs/BP/scripts/classes/Commands/Command.js | 15 ++- .../classes/Commands/lib/commandError.js | 9 -- .../classes/Commands/lib/findInstance.js | 12 --- .../classes/Commands/lib/requirePlayer.js | 8 -- .../classes/Errors/CommandResponseError.js | 14 +++ .../classes/Errors/InstanceExistsError.js | 15 +++ .../classes/Errors/InstanceNotFoundError.js | 15 +++ .../classes/Errors/InvalidInstanceError.js | 6 -- .../classes/Errors/InvalidStructureError.js | 6 -- .../scripts/classes/Errors/NotAPlayerError.js | 8 +- .../classes/Errors/StructureNotFoundError.js | 15 +++ .../classes/Instance/InstanceOptions.js | 1 + .../classes/Materials/StructureMaterials.js | 7 +- packs/BP/scripts/classes/MenuForm.js | 20 ++-- .../classes/Render/StructureOutliner.js | 7 +- .../BP/scripts/classes/Structure/Structure.js | 4 +- .../classes/Structure/StructureCollection.js | 20 ++-- packs/BP/scripts/commands/ActiveCommand.js | 42 ++++----- packs/BP/scripts/commands/ConstructCommand.js | 33 ++++--- packs/BP/scripts/commands/DeleteCommand.js | 24 ++--- packs/BP/scripts/commands/InfoCommand.js | 93 +++++++++++-------- packs/BP/scripts/commands/LayerCommand.js | 35 +++---- packs/BP/scripts/commands/ListCommand.js | 62 ++++++------- packs/BP/scripts/commands/MaterialsCommand.js | 73 ++++++++------- packs/BP/scripts/commands/MoveCommand.js | 63 +++++-------- packs/BP/scripts/commands/NewCommand.js | 54 ++++++----- packs/BP/scripts/commands/NextLayerCommand.js | 24 ++--- packs/BP/scripts/commands/OptionCommand.js | 31 +++---- packs/BP/scripts/commands/PlaceCommand.js | 53 +++++------ packs/BP/scripts/commands/PrevLayerCommand.js | 24 ++--- packs/BP/scripts/commands/RenameCommand.js | 30 ++---- packs/BP/scripts/commands/StatsCommand.js | 40 ++++---- packs/BP/scripts/commands/TagCommand.js | 40 +++----- packs/BP/scripts/commands/VerifierCommand.js | 31 +++---- packs/RP/texts/en_US.lang | 74 ++++++++------- 38 files changed, 527 insertions(+), 525 deletions(-) create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 packs/BP/scripts/classes/Commands/lib/commandError.js delete mode 100644 packs/BP/scripts/classes/Commands/lib/findInstance.js delete mode 100644 packs/BP/scripts/classes/Commands/lib/requirePlayer.js create mode 100644 packs/BP/scripts/classes/Errors/CommandResponseError.js create mode 100644 packs/BP/scripts/classes/Errors/InstanceExistsError.js create mode 100644 packs/BP/scripts/classes/Errors/InstanceNotFoundError.js delete mode 100644 packs/BP/scripts/classes/Errors/InvalidInstanceError.js delete mode 100644 packs/BP/scripts/classes/Errors/InvalidStructureError.js create mode 100644 packs/BP/scripts/classes/Errors/StructureNotFoundError.js diff --git a/.gitignore b/.gitignore index 1cd8c8e..0a8c094 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /build /.regolith docs/ -.claude \ No newline at end of file +.claude +node_modules/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..202f6ac --- /dev/null +++ b/package-lock.json @@ -0,0 +1,36 @@ +{ + "name": "Construct", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@minecraft/server": "^2.8.0-beta.1.26.21-stable" + } + }, + "node_modules/@minecraft/common": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@minecraft/common/-/common-1.3.0.tgz", + "integrity": "sha512-GLT8USFhvEyeTTFHZAgszbrnoT007hmXmK+aO4l+2A1up9/zwZ+4e8R8F0KcKrCWDjLEqkOJtPow0hQCcNJ++Q==", + "license": "MIT", + "peer": true + }, + "node_modules/@minecraft/server": { + "version": "2.8.0-beta.1.26.21-stable", + "resolved": "https://registry.npmjs.org/@minecraft/server/-/server-2.8.0-beta.1.26.21-stable.tgz", + "integrity": "sha512-HdR2EjmleJBBuaXH4iEobdXh7dcuWtV8AIZn2Kd+UkvlBPc4iIPSqXaEt7nqOk0tyhbKfRs/X3aoVtAMHW+kmQ==", + "license": "MIT", + "peerDependencies": { + "@minecraft/common": "^1.2.0", + "@minecraft/vanilla-data": ">=1.20.70" + } + }, + "node_modules/@minecraft/vanilla-data": { + "version": "1.26.21", + "resolved": "https://registry.npmjs.org/@minecraft/vanilla-data/-/vanilla-data-1.26.21.tgz", + "integrity": "sha512-bDmqSIjZBoaChpAdK2H3SVzhIod4/kXwY+viEA3AgftAbda1X1dkjMiBbxZcmwzRrVwT9embIB3zsEkYn1rSNA==", + "license": "MIT", + "peer": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..36f01d7 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@minecraft/server": "^2.8.0-beta.1.26.21-stable" + } +} diff --git a/packs/BP/scripts/classes/Commands/Command.js b/packs/BP/scripts/classes/Commands/Command.js index b774650..84b77dd 100644 --- a/packs/BP/scripts/classes/Commands/Command.js +++ b/packs/BP/scripts/classes/Commands/Command.js @@ -1,4 +1,4 @@ -import { CustomCommandSource, CustomCommandStatus, Player, system } from "@minecraft/server"; +import { CustomCommandParamType, CustomCommandSource, CustomCommandStatus, Player, RawMessageError, system } from "@minecraft/server"; import { Commands } from "./Commands.js"; import { BlockCommandOrigin } from "./BlockCommandOrigin"; import { EntityCommandOrigin } from "./EntityCommandOrigin"; @@ -60,8 +60,15 @@ export class Command { this.callback = (origin, ...args) => { const source = Command.resolveCommandOrigin(origin); if (this.#commandSourceIsNotAllowed(source)) - return { status: CustomCommandStatus.Failure, message: 'commands.generic.invalidsource' }; - return this.customCommand.callback(source, ...args); + return { status: CustomCommandStatus.Failure, message: 'construct.error.invalidCommandSource' }; + try { + return this.customCommand.callback(source, ...args); + } catch (error) { + if (error instanceof RawMessageError) + error.sendTo(source); + else + throw error; + } } } @@ -88,6 +95,8 @@ export class Command { return []; for (const parameter of parameters) { if (parameter.name) + parameter.name = `${parameter.name}`; + if (parameter.type === CustomCommandParamType.Enum) parameter.name = `${PACK_IDENTIFIER}:${parameter.name}`; } return parameters; diff --git a/packs/BP/scripts/classes/Commands/lib/commandError.js b/packs/BP/scripts/classes/Commands/lib/commandError.js deleted file mode 100644 index e0f25f1..0000000 --- a/packs/BP/scripts/classes/Commands/lib/commandError.js +++ /dev/null @@ -1,9 +0,0 @@ -import { CustomCommandStatus } from '@minecraft/server'; -import { NotAPlayerError } from '../../Errors/NotAPlayerError'; - -export function commandError(source, err) { - if (err instanceof NotAPlayerError) { - return { status: CustomCommandStatus.Failure, message: 'construct.commands.error.notAPlayer' }; - } - throw err; -} diff --git a/packs/BP/scripts/classes/Commands/lib/findInstance.js b/packs/BP/scripts/classes/Commands/lib/findInstance.js deleted file mode 100644 index 3019d33..0000000 --- a/packs/BP/scripts/classes/Commands/lib/findInstance.js +++ /dev/null @@ -1,12 +0,0 @@ -import { system } from '@minecraft/server'; -import { structureCollection } from '../../Structure/StructureCollection'; - -export function findInstance(source, name) { - if (!structureCollection.has(name)) { - system.run(() => source.sendMessage({ - rawtext: [{ translate: 'construct.commands.error.instanceNotFound', with: [name] }] - })); - return null; - } - return structureCollection.get(name); -} diff --git a/packs/BP/scripts/classes/Commands/lib/requirePlayer.js b/packs/BP/scripts/classes/Commands/lib/requirePlayer.js deleted file mode 100644 index 1e88e61..0000000 --- a/packs/BP/scripts/classes/Commands/lib/requirePlayer.js +++ /dev/null @@ -1,8 +0,0 @@ -import { PlayerCommandOrigin } from '../PlayerCommandOrigin'; -import { NotAPlayerError } from '../../Errors/NotAPlayerError'; - -export function requirePlayer(source) { - if (!(source instanceof PlayerCommandOrigin)) - throw new NotAPlayerError(); - return source.getSource(); -} diff --git a/packs/BP/scripts/classes/Errors/CommandResponseError.js b/packs/BP/scripts/classes/Errors/CommandResponseError.js new file mode 100644 index 0000000..523ccb9 --- /dev/null +++ b/packs/BP/scripts/classes/Errors/CommandResponseError.js @@ -0,0 +1,14 @@ +export class CommandResponseError extends Error { + constructor(message) { + super(message); + this.name = 'CommandResponseError'; + } + + getRawMessage() { + throw new Error('getRawMessage() must be implemented by subclasses of CommandResponseError'); + } + + sendTo(source) { + source.sendMessage(this.getRawMessage()); + } +} diff --git a/packs/BP/scripts/classes/Errors/InstanceExistsError.js b/packs/BP/scripts/classes/Errors/InstanceExistsError.js new file mode 100644 index 0000000..379912d --- /dev/null +++ b/packs/BP/scripts/classes/Errors/InstanceExistsError.js @@ -0,0 +1,15 @@ +import { CommandResponseError } from "./CommandResponseError"; + +export class InstanceExistsError extends CommandResponseError { + instanceName; + + constructor(instanceName) { + super(`An instance with the name "${instanceName}" already exists.`); + this.name = 'InstanceExistsError'; + this.instanceName = instanceName; + } + + getRawMessage() { + return { translate: 'construct.error.instanceExists', with: [this.instanceName] }; + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Errors/InstanceNotFoundError.js b/packs/BP/scripts/classes/Errors/InstanceNotFoundError.js new file mode 100644 index 0000000..57e7950 --- /dev/null +++ b/packs/BP/scripts/classes/Errors/InstanceNotFoundError.js @@ -0,0 +1,15 @@ +import { CommandResponseError } from "./CommandResponseError"; + +export class InstanceNotFoundError extends CommandResponseError { + instanceName; + + constructor(instanceName) { + super(`§cInstance "${instanceName}" not found.`); + this.name = 'InstanceNotFoundError'; + this.instanceName = instanceName; + } + + getRawMessage() { + return { translate: 'construct.error.instanceNotFound', with: [this.instanceName] }; + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Errors/InvalidInstanceError.js b/packs/BP/scripts/classes/Errors/InvalidInstanceError.js deleted file mode 100644 index 612e2dc..0000000 --- a/packs/BP/scripts/classes/Errors/InvalidInstanceError.js +++ /dev/null @@ -1,6 +0,0 @@ -export class InvalidInstanceError extends Error { - constructor(message) { - super(message); - this.name = 'InvalidInstanceError'; - } -} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Errors/InvalidStructureError.js b/packs/BP/scripts/classes/Errors/InvalidStructureError.js deleted file mode 100644 index 97e88b8..0000000 --- a/packs/BP/scripts/classes/Errors/InvalidStructureError.js +++ /dev/null @@ -1,6 +0,0 @@ -export class InvalidStructureError extends Error { - constructor(message) { - super(message); - this.name = 'InvalidStructureError'; - } -} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Errors/NotAPlayerError.js b/packs/BP/scripts/classes/Errors/NotAPlayerError.js index 252fd9a..836bb7f 100644 --- a/packs/BP/scripts/classes/Errors/NotAPlayerError.js +++ b/packs/BP/scripts/classes/Errors/NotAPlayerError.js @@ -1,6 +1,12 @@ -export class NotAPlayerError extends Error { +import { CommandResponseError } from "./CommandResponseError"; + +export class NotAPlayerError extends CommandResponseError { constructor(message = 'Command requires a player source.') { super(message); this.name = 'NotAPlayerError'; } + + getRawMessage() { + return { translate: 'construct.commands.error.notAPlayer' }; + } } diff --git a/packs/BP/scripts/classes/Errors/StructureNotFoundError.js b/packs/BP/scripts/classes/Errors/StructureNotFoundError.js new file mode 100644 index 0000000..55a0410 --- /dev/null +++ b/packs/BP/scripts/classes/Errors/StructureNotFoundError.js @@ -0,0 +1,15 @@ +import { CommandResponseError } from "./CommandResponseError"; + +export class StructureNotFoundError extends CommandResponseError { + structureId; + + constructor(structureId) { + super(`Structure with ID "${structureId}" not found.`); + this.name = 'StructureNotFoundError'; + this.structureId = structureId; + } + + getRawMessage() { + return { translate: 'construct.error.structureNotFound', with: [this.structureId] }; + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Instance/InstanceOptions.js b/packs/BP/scripts/classes/Instance/InstanceOptions.js index a14550f..a3818f6 100644 --- a/packs/BP/scripts/classes/Instance/InstanceOptions.js +++ b/packs/BP/scripts/classes/Instance/InstanceOptions.js @@ -26,6 +26,7 @@ export class InstanceOptions extends Option { this.instanceName = instanceName; this.structureId = structureId; this.load(); + this.save(); } save() { diff --git a/packs/BP/scripts/classes/Materials/StructureMaterials.js b/packs/BP/scripts/classes/Materials/StructureMaterials.js index a4cc818..377e7ca 100644 --- a/packs/BP/scripts/classes/Materials/StructureMaterials.js +++ b/packs/BP/scripts/classes/Materials/StructureMaterials.js @@ -1,5 +1,6 @@ import { ItemStack, system } from "@minecraft/server"; import { Vector } from "../../lib/Vector"; +import { InstanceNotPlacedError } from "../Errors/InstanceNotPlacedError"; class StructureMaterials { instance; @@ -21,11 +22,11 @@ class StructureMaterials { system.runJob(this.populateActive()); else system.runJob(this.populateAll()); - } catch (e) { - if (e.name === 'InstanceNotPlacedError') + } catch (error) { + if (error instanceof InstanceNotPlacedError) this.clear(); else - throw e; + throw error; } } diff --git a/packs/BP/scripts/classes/MenuForm.js b/packs/BP/scripts/classes/MenuForm.js index dcff275..19a51ce 100644 --- a/packs/BP/scripts/classes/MenuForm.js +++ b/packs/BP/scripts/classes/MenuForm.js @@ -3,6 +3,8 @@ import { structureCollection } from './Structure/StructureCollection'; import { MenuFormBuilder } from './MenuFormBuilder'; import { InstanceForm } from './Instance/InstanceForm'; import { BuilderForm } from './Builder/BuilderForm'; +import { InstanceExistsError } from './Errors/InstanceExistsError'; +import { StructureNotFoundError } from './Errors/StructureNotFoundError'; export class MenuForm { constructor(player, { jumpToInstance = false, instanceName = void 0 } = {}) { @@ -43,12 +45,12 @@ export class MenuForm { return selectedInstanceName || this.createNewInstance(); } }); - } catch (e) { - if (e.message === 'Menu timed out.') { + } catch (error) { + if (error.message === 'Menu timed out.') { this.player.sendMessage({ translate: 'construct.menu.open.timeout' }); return void 0; } - throw e; + throw error; } } @@ -64,16 +66,12 @@ export class MenuForm { return void 0; try { structureCollection.add(instanceName, structureId); - } catch (e) { - if (e.name === 'InvalidInstanceError') { - this.player.sendMessage({ translate: 'construct.mainmenu.instance.exists', with: [instanceName] }); + } catch (error) { + if (error instanceof InstanceExistsError || error instanceof StructureNotFoundError) { + error.sendTo(this.player); return void 0; } - if (e.name === 'InvalidStructureError') { - this.player.sendMessage({ translate: 'construct.mainmenu.instance.notfound', with: [structureId] }); - return void 0; - } - throw e; + throw error; } return instanceName; }); diff --git a/packs/BP/scripts/classes/Render/StructureOutliner.js b/packs/BP/scripts/classes/Render/StructureOutliner.js index 6d7e005..8c1b8ff 100644 --- a/packs/BP/scripts/classes/Render/StructureOutliner.js +++ b/packs/BP/scripts/classes/Render/StructureOutliner.js @@ -1,3 +1,4 @@ +import { StructureNotFoundError } from '../Errors/StructureNotFoundError'; import { Outliner } from '../Outliner'; export class StructureOutliner { @@ -13,11 +14,11 @@ export class StructureOutliner { this.bounds = this.instance.getBounds(); this.bounds.min = this.instance.toGlobalCoords(this.bounds.min); this.bounds.max = this.instance.toGlobalCoords(this.bounds.max); - } catch (e) { - if (e.name === 'InvalidStructureError') + } catch (error) { + if (error instanceof StructureNotFoundError) this.outliner.stopDraw(); else - throw e; + throw error; } } diff --git a/packs/BP/scripts/classes/Structure/Structure.js b/packs/BP/scripts/classes/Structure/Structure.js index 54c2559..64b60d2 100644 --- a/packs/BP/scripts/classes/Structure/Structure.js +++ b/packs/BP/scripts/classes/Structure/Structure.js @@ -1,6 +1,6 @@ import { world } from "@minecraft/server"; import { Vector } from "../../lib/Vector"; -import { InvalidStructureError } from "../Errors/InvalidStructureError"; +import { StructureNotFoundError } from "../Errors/StructureNotFoundError"; export class Structure { structureId; @@ -10,7 +10,7 @@ export class Structure { this.structureId = structureId; this.#structure = world.structureManager.get(structureId); if (!this.#structure) - throw new InvalidStructureError(`[Construct] Structure '${structureId}' not found on world.`); + throw new StructureNotFoundError(structureId); this.#structure.saveToWorld(); } diff --git a/packs/BP/scripts/classes/Structure/StructureCollection.js b/packs/BP/scripts/classes/Structure/StructureCollection.js index 1f4c9dc..10a2eac 100644 --- a/packs/BP/scripts/classes/Structure/StructureCollection.js +++ b/packs/BP/scripts/classes/Structure/StructureCollection.js @@ -1,7 +1,9 @@ -import { InvalidInstanceError } from '../Errors/InvalidInstanceError'; +import { InstanceExistsError } from '../Errors/InstanceExistsError'; +import { InstanceNotFoundError } from '../Errors/InstanceNotFoundError'; +import { StructureNotFoundError } from '../Errors/StructureNotFoundError'; import { InstanceOptions } from '../Instance/InstanceOptions'; import { StructureInstance } from '../Instance/StructureInstance'; -import { world } from '@minecraft/server'; +import { InvalidStructureError, world } from '@minecraft/server'; class StructureCollection { structures; @@ -27,7 +29,7 @@ class StructureCollection { add(instanceName, structureId) { if (this.structures[instanceName]) - throw new InvalidInstanceError(`Instance ${instanceName} already exists.`); + throw new InstanceExistsError(instanceName); const structure = new StructureInstance(instanceName, structureId); this.structures[instanceName] = structure; return structure; @@ -36,7 +38,7 @@ class StructureCollection { get(instanceName) { const structure = this.structures[instanceName]; if (!structure) - throw new InvalidInstanceError(`Instance ${instanceName} not found.`); + throw new InstanceNotFoundError(instanceName); return structure; } @@ -58,12 +60,12 @@ class StructureCollection { return Object.values(this.structures).filter(structure => { try { return structure.isLocationActive(dimensionId, structure.toStructureCoords(location), options) - } catch (e) { - if (e.name === 'InvalidStructureError') { - structureCollection.delete(structure.name); + } catch (error) { + if (error instanceof StructureNotFoundError || error instanceof InvalidStructureError) { + this.delete(structure.name); return false; } else { - throw e; + throw error; } } }); @@ -101,7 +103,7 @@ class StructureCollection { rename(instanceName, newName) { const structure = this.get(instanceName); if (this.structures[newName]) - throw new Error(`Instance '${newName}' already exists.`); + throw new InstanceExistsError(newName); structure.rename(newName); this.structures[newName] = structure; delete this.structures[instanceName]; diff --git a/packs/BP/scripts/commands/ActiveCommand.js b/packs/BP/scripts/commands/ActiveCommand.js index b8a9dbb..a604e7f 100644 --- a/packs/BP/scripts/commands/ActiveCommand.js +++ b/packs/BP/scripts/commands/ActiveCommand.js @@ -1,7 +1,6 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { CustomCommandParamType, CustomCommandStatus, CommandPermissionLevel, system } from '@minecraft/server'; +import { structureCollection } from '../classes/Structure/StructureCollection'; export class ActiveCommand extends Command { constructor() { @@ -12,32 +11,29 @@ export class ActiveCommand extends Command { { name: 'instanceName', type: CustomCommandParamType.String }, { name: 'state', type: CustomCommandParamType.Boolean } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName, state) => this.run(source, instanceName, state) }); } run(source, instanceName, state) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - if (state && !instance.hasLocation()) { - system.run(() => source.sendMessage({ - rawtext: [{ translate: 'construct.commands.error.noLocation', with: [instanceName] }] - })); - return { status: CustomCommandStatus.Failure }; - } - system.run(() => { - if (state) instance.enable(); - else instance.disable(); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.active.success', - with: [instanceName, String(state)] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); + const instance = structureCollection.get(instanceName); + if (state && !instance.hasLocation()) { + source.sendMessage({ translate: 'construct.commands.error.noLocation', with: [instanceName] }); + return void 0; } + system.run(() => { + if (state) + instance.enable(); + else + instance.disable(); + this.sendFeedback(source, instanceName, state); + }); + return { status: CustomCommandStatus.Success }; + } + + sendFeedback(source, instanceName, state) { + source.sendMessage({ translate: state ? 'construct.commands.active.true' : 'construct.commands.active.false', with: [instanceName] }); } } diff --git a/packs/BP/scripts/commands/ConstructCommand.js b/packs/BP/scripts/commands/ConstructCommand.js index 04e4ba2..4a95a53 100644 --- a/packs/BP/scripts/commands/ConstructCommand.js +++ b/packs/BP/scripts/commands/ConstructCommand.js @@ -1,8 +1,6 @@ import { CommandPermissionLevel, CustomCommandStatus, EntityComponentTypes, ItemStack, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; import { PlayerCommandOrigin } from '../classes/Commands/PlayerCommandOrigin'; -import { requirePlayer } from '../classes/Commands/lib/requirePlayer'; -import { commandError } from '../classes/Commands/lib/commandError'; import { MENU_ITEM } from '../consts'; export class ConstructCommand extends Command { @@ -10,28 +8,29 @@ export class ConstructCommand extends Command { super({ name: 'construct', description: 'construct.commands.construct', - permissionLevel: CommandPermissionLevel.Any, cheatsRequired: false, allowedSources: [PlayerCommandOrigin], + permissionLevel: CommandPermissionLevel.Any, callback: (source) => this.run(source) }); } run(source) { - try { - const player = requirePlayer(source); - system.run(() => { - const remaining = player.getComponent(EntityComponentTypes.Inventory) - ?.container?.addItem(new ItemStack(MENU_ITEM)); - if (remaining) - player.sendMessage({ translate: 'construct.commands.construct.fail' }); - else - player.sendMessage({ translate: 'construct.commands.construct.success' }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const player = source.getSource(); + system.run(() => { + this.giveMenuItem(player); + }); + return { status: CustomCommandStatus.Success }; + } + + giveMenuItem(player) { + const inventoryComponent = player.getComponent(EntityComponentTypes.Inventory); + const inventoryContainer = inventoryComponent?.container; + const remaining = inventoryContainer?.addItem(new ItemStack(MENU_ITEM)); + if (remaining) + player.sendMessage({ translate: 'construct.commands.construct.fail' }); + else + player.sendMessage({ translate: 'construct.commands.construct.success' }); } } diff --git a/packs/BP/scripts/commands/DeleteCommand.js b/packs/BP/scripts/commands/DeleteCommand.js index 7799fda..1ce4361 100644 --- a/packs/BP/scripts/commands/DeleteCommand.js +++ b/packs/BP/scripts/commands/DeleteCommand.js @@ -1,8 +1,6 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; +import { CustomCommandParamType, CustomCommandStatus, CommandPermissionLevel, system } from '@minecraft/server'; import { structureCollection } from '../classes/Structure/StructureCollection'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; export class DeleteCommand extends Command { constructor() { @@ -12,24 +10,18 @@ export class DeleteCommand extends Command { mandatoryParameters: [ { name: 'instanceName', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName) => this.run(source, instanceName) }); } run(source, instanceName) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - system.run(() => { - structureCollection.delete(instanceName); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.delete.success', with: [instanceName] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const instance = structureCollection.get(instanceName); + system.run(() => { + structureCollection.delete(instanceName); + source.sendMessage({ translate: 'construct.commands.delete.success', with: [instanceName] }); + }); + return { status: CustomCommandStatus.Success }; } } diff --git a/packs/BP/scripts/commands/InfoCommand.js b/packs/BP/scripts/commands/InfoCommand.js index 155aa4f..486aad1 100644 --- a/packs/BP/scripts/commands/InfoCommand.js +++ b/packs/BP/scripts/commands/InfoCommand.js @@ -1,7 +1,7 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { structureCollection } from '../classes/Structure/StructureCollection'; +import { Vector } from '../lib/Vector'; export class InfoCommand extends Command { constructor() { @@ -11,46 +11,63 @@ export class InfoCommand extends Command { mandatoryParameters: [ { name: 'instanceName', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName) => this.run(source, instanceName) }); } run(source, instanceName) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - const rawtext = [ - { translate: 'construct.commands.info.header', with: [instance.getName()] }, - { text: '\n' }, - { translate: 'construct.commands.info.structure', with: [instance.getStructureId()] }, - { text: '\n' }, - { translate: 'construct.commands.info.enabled', with: [String(instance.isEnabled())] }, - { text: '\n' } - ]; - if (instance.hasLocation()) { - const { dimensionId, location } = instance.getLocation(); - rawtext.push({ translate: 'construct.commands.info.location', - with: [String(location.x), String(location.y), String(location.z), - dimensionId.replace('minecraft:', '')] }); - } else { - rawtext.push({ translate: 'construct.commands.info.noLocation' }); - } - rawtext.push({ text: '\n' }); - rawtext.push({ translate: 'construct.commands.info.layer', - with: [String(instance.getLayer()), String(instance.getMaxLayer())] }); - rawtext.push({ text: '\n' }); - rawtext.push({ translate: 'construct.commands.info.verifier', - with: [String(instance.options.verifier.isEnabled)] }); - rawtext.push({ text: '\n' }); - const bounds = instance.getBounds(); - rawtext.push({ translate: 'construct.commands.info.bounds', - with: [String(bounds.min.x), String(bounds.min.y), String(bounds.min.z), - String(bounds.max.x), String(bounds.max.y), String(bounds.max.z)] }); - system.run(() => source.sendMessage({ rawtext })); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const instance = structureCollection.get(instanceName); + const message = { rawtext: [ + this.getHeaderText(instance), + { text: '\n' }, + this.getStructureIdText(instance), + { text: '\n' }, + this.getLocationText(instance), + { text: '\n' }, + this.getEnabledText(instance), + { text: '\n' }, + this.getLayerText(instance), + { text: '\n' }, + this.getVerifierText(instance), + { text: '\n' }, + this.getSizeText(instance) + ]}; + source.sendMessage(message); + return { status: CustomCommandStatus.Success }; + } + + getHeaderText(instance) { + return { translate: 'construct.commands.info.header', with: [instance.getName()] }; + } + + getStructureIdText(instance) { + return { translate: 'construct.commands.info.structure', with: [instance.getStructureId()] }; + } + + getLocationText(instance) { + if (!instance.hasLocation()) + return { translate: 'construct.commands.info.noLocation' }; + const { dimensionId, location } = instance.getLocation(); + return { translate: 'construct.commands.info.location', with: [location.toString(), dimensionId.replace('minecraft:', '')] }; + } + + getEnabledText(instance) { + return { translate: 'construct.commands.info.enabled', with: [String(instance.isEnabled())] }; + } + + getLayerText(instance) { + return { translate: 'construct.commands.info.layer', with: [String(instance.getLayer()), String(instance.getMaxLayer())] }; + } + + getVerifierText(instance) { + const verifier = instance.options.verifier; + return { translate: 'construct.commands.info.verifier', with: [String(verifier.isEnabled)] }; + } + + getSizeText(instance) { + const bounds = instance.getBounds(); + return { translate: 'construct.commands.info.size', with: [bounds.max.toString(), Vector.volume(bounds.min, bounds.max).toString()] }; } } diff --git a/packs/BP/scripts/commands/LayerCommand.js b/packs/BP/scripts/commands/LayerCommand.js index 5f0ff01..a926fbf 100644 --- a/packs/BP/scripts/commands/LayerCommand.js +++ b/packs/BP/scripts/commands/LayerCommand.js @@ -1,7 +1,6 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; +import { structureCollection } from '../classes/Structure/StructureCollection'; export class LayerCommand extends Command { constructor() { @@ -12,33 +11,21 @@ export class LayerCommand extends Command { { name: 'instanceName', type: CustomCommandParamType.String }, { name: 'layer', type: CustomCommandParamType.Integer } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName, layer) => this.run(source, instanceName, layer) }); } run(source, instanceName, layer) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - const max = instance.getMaxLayer(); - if (layer < 0 || layer > max) { - system.run(() => source.sendMessage({ - rawtext: [{ translate: 'construct.commands.layer.outOfBounds', - with: [String(layer), instanceName, String(max)] }] - })); - return { status: CustomCommandStatus.Failure }; - } - system.run(() => { - instance.setLayer(layer); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.layer.success', - with: [instanceName, String(layer)] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); + const instance = structureCollection.get(instanceName); + const max = instance.getMaxLayer(); + if (layer < 0 || layer > max) { + source.sendMessage({ translate: 'construct.commands.layer.outOfBounds', with: [String(layer), instanceName, String(max)] }); + return void 0; } + instance.setLayer(layer); + source.sendMessage({ translate: 'construct.commands.layer.success', with: [instanceName, String(layer)] }); + return { status: CustomCommandStatus.Success }; } } diff --git a/packs/BP/scripts/commands/ListCommand.js b/packs/BP/scripts/commands/ListCommand.js index bdd1dc9..dccd5e4 100644 --- a/packs/BP/scripts/commands/ListCommand.js +++ b/packs/BP/scripts/commands/ListCommand.js @@ -1,53 +1,51 @@ -import { CustomCommandStatus, system } from '@minecraft/server'; +import { CommandPermissionLevel, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; import { structureCollection } from '../classes/Structure/StructureCollection'; -import { commandError } from '../classes/Commands/lib/commandError'; export class ListCommand extends Command { constructor() { super({ name: 'list', description: 'construct.commands.list', + permissionLevel: CommandPermissionLevel.Any, callback: (source) => this.run(source) }); } run(source) { - try { - const names = structureCollection.getInstanceNames(); - if (names.length === 0) { - return { status: CustomCommandStatus.Success, message: 'construct.commands.list.empty' }; - } - system.run(() => { - const rawtext = [ - { translate: 'construct.commands.list.header', with: [String(names.length)] }, - { text: '\n' } - ]; - for (const name of names) { - const instance = structureCollection.get(name); - const status = this.formatStatus(instance); - rawtext.push({ - translate: 'construct.commands.list.row', - with: [name, instance.getStructureId(), status] - }); - rawtext.push({ text: '\n' }); - } - source.sendMessage({ rawtext }); + const names = structureCollection.getInstanceNames(); + if (names.length === 0) + return { status: CustomCommandStatus.Success, message: 'construct.commands.list.empty' }; + const rawtext = [ + { translate: 'construct.commands.list.header', with: [String(names.length)] }, + { text: '\n' } + ]; + for (const name of names) { + const instance = structureCollection.get(name); + const status = this.formatStatus(instance); + rawtext.push({ + translate: 'construct.commands.list.row', + with: { rawtext: [{ text: name }, { text: instance.getStructureId() }, status] } }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); + rawtext.push({ text: '\n' }); } + source.sendMessage({ rawtext }); + return { status: CustomCommandStatus.Success }; } formatStatus(instance) { - if (!instance.hasLocation()) - return 'no location'; - if (!instance.isEnabled()) - return 'disabled'; - const { dimensionId, location } = instance.getLocation(); - const dim = dimensionId.replace('minecraft:', ''); - return `enabled @ ${location.x} ${location.y} ${location.z} (${dim})`; + const statusMessage = { rawtext: [] }; + if (instance.isEnabled()) + statusMessage.rawtext.push({ translate: 'construct.commands.list.row.enabled' }); + else + statusMessage.rawtext.push({ translate: 'construct.commands.list.row.disabled' }); + if (instance.hasLocation()) { + const { dimensionId, location } = instance.getLocation(); + statusMessage.rawtext.push({ translate: 'construct.commands.list.row.location', with: [location.toString(), dimensionId.replace('minecraft:', '')] }); + } else { + statusMessage.rawtext.push({ translate: 'construct.commands.list.row.nolocation' }); + } + return statusMessage; } } diff --git a/packs/BP/scripts/commands/MaterialsCommand.js b/packs/BP/scripts/commands/MaterialsCommand.js index 2638e56..b150048 100644 --- a/packs/BP/scripts/commands/MaterialsCommand.js +++ b/packs/BP/scripts/commands/MaterialsCommand.js @@ -1,8 +1,8 @@ -import { CustomCommandParamType, CustomCommandStatus, EntityComponentTypes, system } from '@minecraft/server'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, EntityComponentTypes, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { requirePlayer } from '../classes/Commands/lib/requirePlayer'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { structureCollection } from '../classes/Structure/StructureCollection'; +import { PlayerCommandOrigin } from '../classes/Commands/PlayerCommandOrigin'; +import { NotAPlayerError } from '../classes/Errors/NotAPlayerError'; export class MaterialsCommand extends Command { constructor() { @@ -15,44 +15,45 @@ export class MaterialsCommand extends Command { optionalParameters: [ { name: 'missing', type: CustomCommandParamType.Boolean } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName, missing) => this.run(source, instanceName, missing) }); } run(source, instanceName, missing) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - const onlyMissing = missing === true; - let container; - let headerKey; - if (onlyMissing) { - const player = requirePlayer(source); - container = player.getComponent(EntityComponentTypes.Inventory)?.container; - headerKey = 'construct.commands.materials.headerMissing'; - } else { - headerKey = 'construct.commands.materials.headerAll'; - } - system.run(() => { - const materials = instance.getActiveMaterials(); - const materialsMap = onlyMissing - ? materials.getMaterialsDifference(container) - : undefined; - const list = materials.formatString(materialsMap); - const rawtext = [ - { translate: headerKey, with: [instanceName] }, - { text: '\n' } - ]; - if (!list.rawtext || list.rawtext.length === 0) - rawtext.push({ translate: 'construct.commands.materials.empty' }); - else - rawtext.push(list); - source.sendMessage({ rawtext }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); + const instance = structureCollection.get(instanceName); + const onlyMissing = missing === true; + if (onlyMissing) + this.assertIsPlayer(source); + const headerKey = onlyMissing ? 'construct.commands.materials.headerMissing' : 'construct.commands.materials.headerAll'; + const rawtext = [ + { translate: headerKey, with: [instanceName] }, + { text: '\n' } + ]; + const list = this.getMaterialList(source, instance, onlyMissing); + if (!list.rawtext || list.rawtext.length === 0) + rawtext.push({ translate: 'construct.commands.materials.empty' }); + else + rawtext.push(list); + source.sendMessage({ rawtext }); + return { status: CustomCommandStatus.Success }; + } + + assertIsPlayer(source) { + if (!(source instanceof PlayerCommandOrigin)) + throw new NotAPlayerError(); + } + + getMaterialList(source, instance, onlyMissing) { + const materials = instance.getActiveMaterials(); + let container; + if (onlyMissing) { + const player = source.getSource(); + const inventoryComponent = player?.getComponent(EntityComponentTypes.Inventory); + container = inventoryComponent?.container; } + const materialsMap = onlyMissing ? materials.getMaterialsDifference(container) : void 0; + return materials.formatString(materialsMap); } } diff --git a/packs/BP/scripts/commands/MoveCommand.js b/packs/BP/scripts/commands/MoveCommand.js index 455dcc9..88f7099 100644 --- a/packs/BP/scripts/commands/MoveCommand.js +++ b/packs/BP/scripts/commands/MoveCommand.js @@ -1,10 +1,8 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system, world } from '@minecraft/server'; +import { Vector } from '../lib/Vector'; +import { structureCollection } from '../classes/Structure/StructureCollection'; import { PlayerCommandOrigin } from '../classes/Commands/PlayerCommandOrigin'; -import { BlockCommandOrigin } from '../classes/Commands/BlockCommandOrigin'; -import { EntityCommandOrigin } from '../classes/Commands/EntityCommandOrigin'; export class MoveCommand extends Command { constructor() { @@ -15,47 +13,34 @@ export class MoveCommand extends Command { { name: 'instanceName', type: CustomCommandParamType.String } ], optionalParameters: [ - { name: 'pos', type: CustomCommandParamType.Location } + { name: 'dimensionId', type: CustomCommandParamType.Enum }, // Enum defined in PlaceCommand.js + { name: 'location', type: CustomCommandParamType.Location } ], - callback: (source, instanceName, pos) => this.run(source, instanceName, pos) + permissionLevel: CommandPermissionLevel.Any, + callback: (source, instanceName, dimensionId, location) => this.run(source, instanceName, dimensionId, location) }); } - run(source, instanceName, pos) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - let dimensionId; - let location = pos; - if (location === undefined) { - if (!(source instanceof PlayerCommandOrigin)) - return { status: CustomCommandStatus.Failure, message: 'construct.commands.move.posRequired' }; - const player = source.getSource(); - location = player.location; - dimensionId = player.dimension.id; - } else { - dimensionId = this.resolveDimensionId(source); - } - system.run(() => { - instance.move(dimensionId, location); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.move.success', - with: [instanceName, String(Math.floor(location.x)), String(Math.floor(location.y)), - String(Math.floor(location.z)), dimensionId.replace('minecraft:', '')] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); + run(source, instanceName, dimensionId, location) { + const instance = structureCollection.get(instanceName); + if (dimensionId === void 0 || location === void 0) { + if (!(source instanceof PlayerCommandOrigin)) + return { status: CustomCommandStatus.Failure, message: 'construct.commands.move.locationRequired' }; + const player = source.getSource(); + location = player.location; + dimensionId = player.dimension.id; } + this.assertDimensionExists(dimensionId); + const flooredLocation = Vector.from(location).floor(); + system.run(() => { + instance.move(dimensionId, flooredLocation); + source.sendMessage({ translate: 'construct.commands.move.success', with: [instanceName, flooredLocation.toString(), dimensionId.replace('minecraft:', '')] }); + }); + return { status: CustomCommandStatus.Success }; } - resolveDimensionId(source) { - if (source instanceof PlayerCommandOrigin || source instanceof EntityCommandOrigin) - return source.getSource().dimension.id; - if (source instanceof BlockCommandOrigin) - return source.getSource().dimension.id; - return 'minecraft:overworld'; + assertDimensionExists(dimensionId) { + return world.getDimension(dimensionId) !== void 0; } } diff --git a/packs/BP/scripts/commands/NewCommand.js b/packs/BP/scripts/commands/NewCommand.js index 977f122..4499fec 100644 --- a/packs/BP/scripts/commands/NewCommand.js +++ b/packs/BP/scripts/commands/NewCommand.js @@ -1,7 +1,8 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { structureCollection } from '../classes/Structure/StructureCollection'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { InstanceExistsError } from '../classes/Errors/InstanceExistsError'; +import { StructureNotFoundError } from '../classes/Errors/StructureNotFoundError'; export class NewCommand extends Command { constructor() { @@ -12,35 +13,38 @@ export class NewCommand extends Command { { name: 'instanceName', type: CustomCommandParamType.String }, { name: 'structureId', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName, structureId) => this.run(source, instanceName, structureId) }); } run(source, instanceName, structureId) { - try { - if (structureCollection.has(instanceName)) { - system.run(() => source.sendMessage({ - rawtext: [{ translate: 'construct.commands.new.duplicateName', with: [instanceName] }] - })); - return { status: CustomCommandStatus.Failure }; - } - if (!structureCollection.getWorldStructureIds().includes(structureId)) { - system.run(() => source.sendMessage({ - rawtext: [{ translate: 'construct.commands.new.unknownStructure', with: [structureId] }] - })); - return { status: CustomCommandStatus.Failure }; - } - system.run(() => { - structureCollection.add(instanceName, structureId); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.new.success', with: [instanceName, structureId] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + this.tryAddStructure(source, instanceName, structureId); + return { status: CustomCommandStatus.Success }; } + + tryAddStructure(source, instanceName, structureId) { + system.run(() => { + try { + this.addStructure(source, instanceName, structureId); + } catch (error) { + this.handleStructureAdditionErrors(source, error); + } + }); + } + + addStructure(source, instanceName, structureId) { + structureCollection.add(instanceName, structureId); + source.sendMessage({ translate: 'construct.commands.new.success', with: [instanceName, structureId] }); + } + + handleStructureAdditionErrors(source, error) { + if (error instanceof InstanceExistsError || error instanceof StructureNotFoundError) + error.sendTo(source); + else + throw error; + } + } export const newCommand = new NewCommand(); diff --git a/packs/BP/scripts/commands/NextLayerCommand.js b/packs/BP/scripts/commands/NextLayerCommand.js index fa4684c..6622dc9 100644 --- a/packs/BP/scripts/commands/NextLayerCommand.js +++ b/packs/BP/scripts/commands/NextLayerCommand.js @@ -1,7 +1,6 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { structureCollection } from '../classes/Structure/StructureCollection'; export class NextLayerCommand extends Command { constructor() { @@ -11,25 +10,16 @@ export class NextLayerCommand extends Command { mandatoryParameters: [ { name: 'instanceName', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName) => this.run(source, instanceName) }); } run(source, instanceName) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - system.run(() => { - instance.increaseLayer(); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.nextlayer.success', - with: [instanceName, String(instance.getLayer())] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const instance = structureCollection.get(instanceName); + instance.increaseLayer(); + source.sendMessage({ translate: 'construct.commands.nextlayer.success', with: [instanceName, String(instance.getLayer())] }); + return { status: CustomCommandStatus.Success }; } } diff --git a/packs/BP/scripts/commands/OptionCommand.js b/packs/BP/scripts/commands/OptionCommand.js index dba4ef6..ac05107 100644 --- a/packs/BP/scripts/commands/OptionCommand.js +++ b/packs/BP/scripts/commands/OptionCommand.js @@ -1,9 +1,7 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; import { PlayerCommandOrigin } from '../classes/Commands/PlayerCommandOrigin'; import { BuilderOptions } from '../classes/Builder/BuilderOptions'; -import { requirePlayer } from '../classes/Commands/lib/requirePlayer'; -import { commandError } from '../classes/Commands/lib/commandError'; export class OptionCommand extends Command { constructor() { @@ -18,29 +16,22 @@ export class OptionCommand extends Command { enums: [ { name: 'optionId', values: ['easyPlace', 'fastEasyPlace', 'materialGrabber'] } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, optionId, state) => this.run(source, optionId, state) }); } run(source, optionId, state) { - try { - const player = requirePlayer(source); - if (!BuilderOptions.get(optionId)) { - system.run(() => source.sendMessage({ - rawtext: [{ translate: 'construct.commands.option.unknownOption', with: [optionId] }] - })); - return { status: CustomCommandStatus.Failure }; - } - system.run(() => { - BuilderOptions.setValue(optionId, player.id, state); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.option.success', with: [optionId, String(state)] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); + if (!BuilderOptions.get(optionId)) { + source.sendMessage({ translate: 'construct.commands.option.unknownOption', with: [optionId] }); + return void 0; } + system.run(() => { + const player = source.getSource(); + BuilderOptions.setValue(optionId, player.id, state); + source.sendMessage({ translate: 'construct.commands.option.success', with: [optionId, String(state)] }); + }); + return { status: CustomCommandStatus.Success }; } } diff --git a/packs/BP/scripts/commands/PlaceCommand.js b/packs/BP/scripts/commands/PlaceCommand.js index 2f9cb34..99e63d4 100644 --- a/packs/BP/scripts/commands/PlaceCommand.js +++ b/packs/BP/scripts/commands/PlaceCommand.js @@ -1,49 +1,38 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; -import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; -import { PlayerCommandOrigin } from '../classes/Commands/PlayerCommandOrigin'; -import { BlockCommandOrigin } from '../classes/Commands/BlockCommandOrigin'; -import { EntityCommandOrigin } from '../classes/Commands/EntityCommandOrigin'; +import { Command } from '../classes/Commands/Command'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, DimensionTypes, system, world } from '@minecraft/server'; +import { structureCollection } from '../classes/Structure/StructureCollection'; +import { InstanceExistsError } from '../classes/Errors/InstanceExistsError'; +import { Vector } from '../lib/Vector'; export class PlaceCommand extends Command { constructor() { super({ name: 'place', description: 'construct.commands.place', + enums: [ { name: 'dimensionId', values: Object.values(DimensionTypes.getAll().map(d => d.typeId)) } ], mandatoryParameters: [ { name: 'instanceName', type: CustomCommandParamType.String }, - { name: 'pos', type: CustomCommandParamType.Location } + { name: 'dimensionId', type: CustomCommandParamType.Enum }, + { name: 'location', type: CustomCommandParamType.Location } ], - callback: (source, instanceName, pos) => this.run(source, instanceName, pos) + permissionLevel: CommandPermissionLevel.Any, + callback: (source, instanceName, dimensionId, location) => this.run(source, instanceName, dimensionId, location) }); } - run(source, instanceName, pos) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - const dimensionId = this.resolveDimensionId(source); - system.run(() => { - instance.place(dimensionId, pos); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.place.success', - with: [instanceName, String(pos.x), String(pos.y), String(pos.z), - dimensionId.replace('minecraft:', '')] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + run(source, instanceName, dimensionId, location) { + const instance = structureCollection.get(instanceName); + const flooredLocation = Vector.from(location).floor(); + this.assertDimensionExists(dimensionId); + system.run(() => { + instance.place(dimensionId, flooredLocation); + source.sendMessage({ translate: 'construct.commands.place.success', with: [instanceName, flooredLocation.toString(), dimensionId.replace('minecraft:', '')] }); + }); + return { status: CustomCommandStatus.Success }; } - resolveDimensionId(source) { - if (source instanceof PlayerCommandOrigin || source instanceof EntityCommandOrigin) - return source.getSource().dimension.id; - if (source instanceof BlockCommandOrigin) - return source.getSource().dimension.id; - return 'minecraft:overworld'; + assertDimensionExists(dimensionId) { + return world.getDimension(dimensionId) !== void 0; } } diff --git a/packs/BP/scripts/commands/PrevLayerCommand.js b/packs/BP/scripts/commands/PrevLayerCommand.js index 1f9d395..7fc485e 100644 --- a/packs/BP/scripts/commands/PrevLayerCommand.js +++ b/packs/BP/scripts/commands/PrevLayerCommand.js @@ -1,7 +1,6 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { structureCollection } from '../classes/Structure/StructureCollection'; export class PrevLayerCommand extends Command { constructor() { @@ -11,25 +10,16 @@ export class PrevLayerCommand extends Command { mandatoryParameters: [ { name: 'instanceName', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName) => this.run(source, instanceName) }); } run(source, instanceName) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - system.run(() => { - instance.decreaseLayer(); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.prevlayer.success', - with: [instanceName, String(instance.getLayer())] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const instance = structureCollection.get(instanceName); + instance.decreaseLayer(); + source.sendMessage({ translate: 'construct.commands.prevlayer.success', with: [instanceName, String(instance.getLayer())] }); + return { status: CustomCommandStatus.Success }; } } diff --git a/packs/BP/scripts/commands/RenameCommand.js b/packs/BP/scripts/commands/RenameCommand.js index 8758760..e915a4e 100644 --- a/packs/BP/scripts/commands/RenameCommand.js +++ b/packs/BP/scripts/commands/RenameCommand.js @@ -1,8 +1,6 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { structureCollection } from '../classes/Structure/StructureCollection'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; export class RenameCommand extends Command { constructor() { @@ -13,30 +11,20 @@ export class RenameCommand extends Command { { name: 'instanceName', type: CustomCommandParamType.String }, { name: 'newName', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName, newName) => this.run(source, instanceName, newName) }); } run(source, instanceName, newName) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - if (structureCollection.has(newName)) { - system.run(() => source.sendMessage({ - rawtext: [{ translate: 'construct.commands.rename.duplicateName', with: [newName] }] - })); - return { status: CustomCommandStatus.Failure }; - } - system.run(() => { - structureCollection.rename(instanceName, newName); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.rename.success', with: [instanceName, newName] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); + const instance = structureCollection.get(instanceName); + if (structureCollection.has(newName)) { + source.sendMessage({ translate: 'construct.error.instanceExists', with: [newName] }); + return void 0; } + structureCollection.rename(instanceName, newName); + source.sendMessage({ translate: 'construct.commands.rename.success', with: [instanceName, newName] }); + return { status: CustomCommandStatus.Success }; } } diff --git a/packs/BP/scripts/commands/StatsCommand.js b/packs/BP/scripts/commands/StatsCommand.js index c68de26..a117e38 100644 --- a/packs/BP/scripts/commands/StatsCommand.js +++ b/packs/BP/scripts/commands/StatsCommand.js @@ -1,8 +1,9 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system, TicksPerSecond } from '@minecraft/server'; import { InstanceFormBuilder } from '../classes/Instance/InstanceFormBuilder'; +import { structureCollection } from '../classes/Structure/StructureCollection'; +import { StructureVerifier } from '../classes/Verifier/StructureVerifier'; +import { StructureStatistics } from '../classes/Structure/StructureStatistics'; export class StatsCommand extends Command { constructor() { @@ -12,26 +13,29 @@ export class StatsCommand extends Command { mandatoryParameters: [ { name: 'instanceName', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName) => this.run(source, instanceName) }); } run(source, instanceName) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - system.run(async () => { - try { - const { stats } = await InstanceFormBuilder.buildStatistics(instance); - source.sendMessage(stats); - } catch (err) { - source.sendMessage({ translate: 'construct.commands.stats.alreadyRunning' }); - } - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const instance = structureCollection.get(instanceName); + if (this.structureVerifier) + return { status: CustomCommandStatus.Failure, error: 'construct.commands.stats.alreadyRunning' }; + system.run(async () => { + source.sendMessage(await this.getStatsMessage(instance)); + }); + return { status: CustomCommandStatus.Success }; + } + + async getStatsMessage(instance) { + const verifierOptions = { isEnabled: true, particleLifetime: 1*TicksPerSecond, isStandalone: true }; + this.structureVerifier = new StructureVerifier(instance, verifierOptions); + const verification = await this.structureVerifier.verifyStructure(true); + const statistics = new StructureStatistics(instance, verification); + const statsMessage = statistics.getMessage(); + this.structureVerifier = void 0; + return statsMessage; } } diff --git a/packs/BP/scripts/commands/TagCommand.js b/packs/BP/scripts/commands/TagCommand.js index 18b076a..663ed1a 100644 --- a/packs/BP/scripts/commands/TagCommand.js +++ b/packs/BP/scripts/commands/TagCommand.js @@ -1,10 +1,8 @@ -import { CustomCommandParamType, CustomCommandStatus, EntityComponentTypes, EquipmentSlot, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, EntityComponentTypes, EquipmentSlot, system } from '@minecraft/server'; import { PlayerCommandOrigin } from '../classes/Commands/PlayerCommandOrigin'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { requirePlayer } from '../classes/Commands/lib/requirePlayer'; -import { commandError } from '../classes/Commands/lib/commandError'; import { MENU_ITEM } from '../consts'; +import { structureCollection } from '../classes/Structure/StructureCollection'; export class TagCommand extends Command { constructor() { @@ -15,32 +13,24 @@ export class TagCommand extends Command { mandatoryParameters: [ { name: 'instanceName', type: CustomCommandParamType.String } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName) => this.run(source, instanceName) }); } run(source, instanceName) { - try { - const player = requirePlayer(source); - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - const equipment = player.getComponent(EntityComponentTypes.Equippable); - const itemStack = equipment?.getEquipment(EquipmentSlot.Mainhand); - if (itemStack?.typeId !== MENU_ITEM) { - system.run(() => source.sendMessage({ translate: 'construct.commands.tag.notHoldingItem' })); - return { status: CustomCommandStatus.Failure }; - } - system.run(() => { - itemStack.nameTag = instanceName; - equipment.setEquipment(EquipmentSlot.Mainhand, itemStack); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.tag.success', with: [instanceName] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const instance = structureCollection.get(instanceName); + const player = source.getSource(); + const equipment = player.getComponent(EntityComponentTypes.Equippable); + const itemStack = equipment?.getEquipment(EquipmentSlot.Mainhand); + if (itemStack?.typeId !== MENU_ITEM) + return { status: CustomCommandStatus.Failure, message: 'construct.commands.tag.notHoldingItem' }; + system.run(() => { + itemStack.nameTag = instanceName; + equipment.setEquipment(EquipmentSlot.Mainhand, itemStack); + source.sendMessage({ translate: 'construct.commands.tag.success', with: [instanceName] }); + }); + return { status: CustomCommandStatus.Success }; } } diff --git a/packs/BP/scripts/commands/VerifierCommand.js b/packs/BP/scripts/commands/VerifierCommand.js index 00ae70d..2d362b2 100644 --- a/packs/BP/scripts/commands/VerifierCommand.js +++ b/packs/BP/scripts/commands/VerifierCommand.js @@ -1,7 +1,6 @@ -import { CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; +import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system } from '@minecraft/server'; import { Command } from '../classes/Commands/Command'; -import { findInstance } from '../classes/Commands/lib/findInstance'; -import { commandError } from '../classes/Commands/lib/commandError'; +import { structureCollection } from '../classes/Structure/StructureCollection'; export class VerifierCommand extends Command { constructor() { @@ -12,25 +11,23 @@ export class VerifierCommand extends Command { { name: 'instanceName', type: CustomCommandParamType.String }, { name: 'state', type: CustomCommandParamType.Boolean } ], + permissionLevel: CommandPermissionLevel.Any, callback: (source, instanceName, state) => this.run(source, instanceName, state) }); } run(source, instanceName, state) { - try { - const instance = findInstance(source, instanceName); - if (!instance) return { status: CustomCommandStatus.Failure }; - system.run(() => { - instance.setVerifierEnabled(state); - source.sendMessage({ - rawtext: [{ translate: 'construct.commands.verifier.success', - with: [instanceName, String(state)] }] - }); - }); - return { status: CustomCommandStatus.Success }; - } catch (err) { - return commandError(source, err); - } + const instance = structureCollection.get(instanceName); + if (state) + instance.setVerifierEnabled(true); + else + instance.setVerifierEnabled(false); + this.sendFeedback(source, instanceName, state); + return { status: CustomCommandStatus.Success }; + } + + sendFeedback(source, instanceName, state) { + source.sendMessage({ translate: state ? 'construct.commands.verifier.enabled' : 'construct.commands.verifier.disabled', with: [instanceName] }); } } diff --git a/packs/RP/texts/en_US.lang b/packs/RP/texts/en_US.lang index 27c0b17..b29a15e 100644 --- a/packs/RP/texts/en_US.lang +++ b/packs/RP/texts/en_US.lang @@ -57,15 +57,6 @@ construct.structure.statistics.stateincorrect=§7Block State Incorrect: §e%s ## construct.structure.statistics.incorrect=§7Incorrect: §c%s ## Insert string: number of incorrectly placed blocks construct.structure.statistics.missing=§7Missing: §3%s ## Insert string: number of missing blocks -## Structure Block Info Display -construct.blockinfo.header=Structure: -construct.blockinfo.none=§7None -construct.blockinfo.nosupply= §c[No Supply] ## When the targeted block is not in the player's inventory -construct.blockinfo.unknown=§7Unknown -construct.blockinfo.waterlogged=§7isWaterlogged: §3true - -construct.mainmenu.instance.exists=§cInstance '%s' already exists. Try again with a new name. ## Insert string: instance name -construct.mainmenu.instance.notfound=§cStructure ID '%s' not found. If you're looking for a structure that you put in the structures folder, please restart your world and try again. ## Insert string: structure name construct.mainmenu.title=§l§2Construct ## This is the name of the pack. construct.mainmenu.selectinstance=Select an instance: construct.mainmenu.howto=How to Add/Remove Structures @@ -84,12 +75,14 @@ construct.mainmenu.howto.add.mcstructure=§7To transfer structures §fbetween wo construct.mainmenu.howto.remove.header=§cHow to Remove Structures: construct.mainmenu.howto.remove.body=§7- Use the §f/structure delete§7 command to remove a structure from the world. -## Commands -construct.commands.construct=Gives you the Construct item. Use it to open the Construct menu. -construct.commands.construct.fail=§cFailed to give you the Construct item. -construct.commands.construct.success=§aYou recieved the Construct item! Use it to open the Construct menu. +## Structure Block Info Display +construct.blockinfo.header=Structure: +construct.blockinfo.none=§7None +construct.blockinfo.nosupply= §c[No Supply] ## When the targeted block is not in the player's inventory +construct.blockinfo.unknown=§7Unknown +construct.blockinfo.waterlogged=§7isWaterlogged: §3true -## Options +## Builder Options construct.option.enabled= is now enabled! construct.option.disabled= is now disabled. construct.option.easyplace.name=Easy Place @@ -106,14 +99,14 @@ construct.option.materialgrabber.grabbed.zero=§7Grabbed 0 items. construct.option.materialgrabber.grabbed.one=§aGrabbed 1 item. construct.option.materialgrabber.grabbed.many=§aGrabbed %s items. ## Insert string: number of items transferred to the player -## CLI shared errors -construct.commands.error.instanceNotFound=§cInstance "%1" not found. -construct.commands.error.notAPlayer=§cThis command can only be used by players. +## Commands +construct.commands.construct=Gives you the Construct item. Use it to open the Construct menu. +construct.commands.construct.fail=§cFailed to give you the Construct item. +construct.commands.construct.success=§aYou recieved the Construct item! Use it to open the Construct menu. ## construct:new construct.commands.new=Create a new instance bound to a structure. construct.commands.new.success=§aCreated instance "%1" bound to structure "%2". -construct.commands.new.duplicateName=§cAn instance named "%1" already exists. construct.commands.new.unknownStructure=§cNo structure with id "%1" found in the world. ## construct:delete @@ -123,30 +116,34 @@ construct.commands.delete.success=§aDeleted instance "%1". ## construct:rename construct.commands.rename=Rename an existing instance. construct.commands.rename.success=§aRenamed instance "%1" to "%2". -construct.commands.rename.duplicateName=§cAn instance named "%1" already exists. ## construct:list construct.commands.list=List all registered instances. construct.commands.list.empty=§7No instances registered. -construct.commands.list.header=§eRegistered instances (%1):§r +construct.commands.list.header=§aRegistered instances (%1):§r construct.commands.list.row=§a%1§r §8[§r%2§8]§r §7%3§r +construct.commands.list.row.enabled=enabled +construct.commands.list.row.disabled=disabled +construct.commands.list.row.location= at %1 in %2 +construct.commands.list.row.nolocation= (no location) ## construct:place construct.commands.place=Place an instance at world coordinates (enables it). -construct.commands.place.success=§aPlaced instance "%1" at %2 %3 %4 in %5. +construct.commands.place.success=§aPlaced instance "%1" at %2 in %3. ## construct:move construct.commands.move=Reposition an instance without toggling its enabled state. -construct.commands.move.success=§aMoved instance "%1" to %2 %3 %4 in %5. -construct.commands.move.posRequired=§cMust provide coordinates when not running as a player. +construct.commands.move.success=§aMoved instance "%1" to %2 in %3. +construct.commands.move.locationRequired=§cMust provide a dimension and coordinates when not running as a player. ## construct:active construct.commands.active=Enable or disable an instance. -construct.commands.active.success=§aSet instance "%1" active=%2. +construct.commands.active.true=§aEnabled instance "%1". +construct.commands.active.false=§aDisabled instance "%1". construct.commands.error.noLocation=§cInstance "%1" has no saved location. ## construct:layer -construct.commands.layer=Set the active layer of an instance (0 = whole structure). +construct.commands.layer=Set the active layer of an instance (Use 0 for the whole structure). construct.commands.layer.success=§aSet instance "%1" layer to %2. construct.commands.layer.outOfBounds=§cLayer %1 is out of bounds for instance "%2" (max %3). @@ -160,7 +157,8 @@ construct.commands.prevlayer.success=§aInstance "%1" stepped back to layer %2. ## construct:verifier construct.commands.verifier=Toggle block validation overlay for an instance. -construct.commands.verifier.success=§aSet instance "%1" verifier=%2. +construct.commands.verifier.enabled=§aEnabled verifier for instance "%1". +construct.commands.verifier.disabled=§aDisabled verifier for instance "%1". ## construct:option construct.commands.option=Toggle a per-player builder option. @@ -169,26 +167,34 @@ construct.commands.option.unknownOption=§cUnknown option "%1". ## construct:info construct.commands.info=Print instance details to chat. -construct.commands.info.header=§e=== Instance "%1" ===§r +construct.commands.info.header=§a=== Instance Info for "%1" ===§r construct.commands.info.structure=§7Structure:§r %1 -construct.commands.info.enabled=§7Enabled:§r %1 -construct.commands.info.location=§7Location:§r %1 %2 %3 in %4 +construct.commands.info.location=§7Location:§r %1 in %2 construct.commands.info.noLocation=§7Location:§r (none) +construct.commands.info.enabled=§7Enabled:§r %1 construct.commands.info.layer=§7Layer:§r %1 / %2 -construct.commands.info.verifier=§7Verifier:§r %1 -construct.commands.info.bounds=§7Bounds:§r (%1, %2, %3) -> (%4, %5, %6) +construct.commands.info.verifier=§7Verifier running:§r %1 +construct.commands.info.size=§7Size:§r %1 (%2 blocks) ## construct:stats construct.commands.stats=Run the structure verifier and print statistics. -construct.commands.stats.alreadyRunning=§cA verification is already in progress. +construct.commands.stats.alreadyRunning=§cA verification is already in progress. Please wait until it finishes. ## construct:materials construct.commands.materials=Print the material list for an instance. -construct.commands.materials.headerAll=§eMaterials for "%1":§r -construct.commands.materials.headerMissing=§eMissing materials for "%1":§r +construct.commands.materials.headerAll=§aMaterials for "%1":§r +construct.commands.materials.headerMissing=§aMissing materials for "%1":§r construct.commands.materials.empty=§7(no materials) ## construct:tag construct.commands.tag=Rename the held Construct item to an instance name for quick-open. construct.commands.tag.success=§aTagged held Construct item with instance "%1". construct.commands.tag.notHoldingItem=§cYou must be holding a Construct item. + +## Errors +construct.error.invalidCommandSource=§cThis command cannot be run from this source. +construct.error.instanceNotFound=§cInstance "%1" not found. ## Insert string: instance name +construct.error.instanceExists=§cInstance "%1" already exists. Try again with a new name. ## Insert string: instance name +construct.error.structureNotFound=§cStructure ID "%1" not found. If you're looking for a structure that you put in the structures folder, please restart your world and try again. ## Insert string: structure name +construct.error.dimensionNotFound=§cDimension "%1" not found. ## Insert string: dimension name +construct.error.notAPlayer=§cThis command can only be used by players. \ No newline at end of file