From 19cbceb1207396093cabc9e7d2bbe39d4cd914c6 Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Wed, 20 May 2026 03:26:25 -0700 Subject: [PATCH] feat(cli): add Command pipeline base classes The Command base class, Commands registry, FeedbackMessageType enum, and four CommandOrigin subclasses (Player/Entity/Block/Server) that every CLI command in this branch depends on. Without these files the new commands fail to resolve imports at module load. Co-Authored-By: Claude Opus 4.7 --- .../classes/Commands/BlockCommandOrigin.js | 12 ++ packs/BP/scripts/classes/Commands/Command.js | 108 ++++++++++++++++++ .../scripts/classes/Commands/CommandOrigin.js | 20 ++++ packs/BP/scripts/classes/Commands/Commands.js | 15 +++ .../classes/Commands/EntityCommandOrigin.js | 13 +++ .../classes/Commands/FeedbackMessageType.js | 7 ++ .../classes/Commands/PlayerCommandOrigin.js | 17 +++ .../classes/Commands/ServerCommandOrigin.js | 13 +++ 8 files changed, 205 insertions(+) create mode 100644 packs/BP/scripts/classes/Commands/BlockCommandOrigin.js create mode 100644 packs/BP/scripts/classes/Commands/Command.js create mode 100644 packs/BP/scripts/classes/Commands/CommandOrigin.js create mode 100644 packs/BP/scripts/classes/Commands/Commands.js create mode 100644 packs/BP/scripts/classes/Commands/EntityCommandOrigin.js create mode 100644 packs/BP/scripts/classes/Commands/FeedbackMessageType.js create mode 100644 packs/BP/scripts/classes/Commands/PlayerCommandOrigin.js create mode 100644 packs/BP/scripts/classes/Commands/ServerCommandOrigin.js diff --git a/packs/BP/scripts/classes/Commands/BlockCommandOrigin.js b/packs/BP/scripts/classes/Commands/BlockCommandOrigin.js new file mode 100644 index 0000000..33814bd --- /dev/null +++ b/packs/BP/scripts/classes/Commands/BlockCommandOrigin.js @@ -0,0 +1,12 @@ +import { CommandOrigin } from "./CommandOrigin"; +import { FeedbackMessageType } from "./FeedbackMessageType"; + +export class BlockCommandOrigin extends CommandOrigin { + getSource() { + return this.source.sourceBlock; + } + + sendMessage() { + return FeedbackMessageType.None; + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Commands/Command.js b/packs/BP/scripts/classes/Commands/Command.js new file mode 100644 index 0000000..b774650 --- /dev/null +++ b/packs/BP/scripts/classes/Commands/Command.js @@ -0,0 +1,108 @@ +import { CustomCommandSource, CustomCommandStatus, Player, system } from "@minecraft/server"; +import { Commands } from "./Commands.js"; +import { BlockCommandOrigin } from "./BlockCommandOrigin"; +import { EntityCommandOrigin } from "./EntityCommandOrigin"; +import { ServerCommandOrigin } from "./ServerCommandOrigin"; +import { PlayerCommandOrigin } from "./PlayerCommandOrigin"; +import { PACK_IDENTIFIER } from "../../consts.js"; + +export class Command { + customCommand; + + static resolveCommandOrigin(origin) { + switch (origin.sourceType) { + case CustomCommandSource.Block: + return new BlockCommandOrigin(origin); + case CustomCommandSource.Entity: + if (origin.sourceEntity instanceof Player) + return new PlayerCommandOrigin(origin); + return new EntityCommandOrigin(origin); + case CustomCommandSource.Server: + return new ServerCommandOrigin(origin); + default: + throw new Error("Unknown command source: " + origin?.sourceType); + } + } + + constructor(customCommand) { + this.customCommand = customCommand; + this.#setDefaultArgs(); + Commands.register(this); + system.beforeEvents.startup.subscribe(this.setupForRegistry.bind(this)); + } + + getName() { + return this.customCommand.name.replace(/^[^:]+:/, ''); + } + + isCheatsRequired() { + return this.customCommand.cheatsRequired; + } + + setupForRegistry(startupEvent) { + this.#registerCommand(startupEvent.customCommandRegistry); + system.beforeEvents.startup.unsubscribe(this.setupForRegistry.bind(this)); + } + + #registerCommand(customCommandRegistry) { + this.#addPreCallback(); + this.#registerEnums(customCommandRegistry); + this.#registerSingleCommand(customCommandRegistry); + this.#registerAliasCommands(customCommandRegistry); + } + + #setDefaultArgs() { + if (this.customCommand.cheatsRequired === void 0) + this.customCommand.cheatsRequired = false; + } + + #addPreCallback() { + 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); + } + } + + #registerEnums(customCommandRegistry) { + if (this.customCommand.enums) { + for (const customEnum of this.customCommand.enums) + customCommandRegistry.registerEnum(`${PACK_IDENTIFIER}:${customEnum.name}`, customEnum.values); + } + } + + #registerSingleCommand(customCommandRegistry, name = this.customCommand.name) { + customCommandRegistry.registerCommand({ + name: `${PACK_IDENTIFIER}:${name}`, + description: this.customCommand.description, + permissionLevel: this.customCommand.permissionLevel, + mandatoryParameters: this.#prepParameters(this.customCommand.mandatoryParameters), + optionalParameters: this.#prepParameters(this.customCommand.optionalParameters), + cheatsRequired: this.customCommand.cheatsRequired + }, this.callback); + } + + #prepParameters(parameters) { + if (!parameters) + return []; + for (const parameter of parameters) { + if (parameter.name) + parameter.name = `${PACK_IDENTIFIER}:${parameter.name}`; + } + return parameters; + } + + #registerAliasCommands(customCommandRegistry) { + if (this.customCommand.aliases) { + for (const alias of this.customCommand.aliases) + this.#registerSingleCommand(customCommandRegistry, alias); + } + } + + #commandSourceIsNotAllowed(source) { + if (!this.customCommand.allowedSources) + return false; + return !this.customCommand.allowedSources.includes(source.constructor); + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Commands/CommandOrigin.js b/packs/BP/scripts/classes/Commands/CommandOrigin.js new file mode 100644 index 0000000..140bffc --- /dev/null +++ b/packs/BP/scripts/classes/Commands/CommandOrigin.js @@ -0,0 +1,20 @@ +import { FeedbackMessageType } from "./FeedbackMessageType"; + +export class CommandOrigin { + constructor(source) { + this.source = source; + } + + getType() { + return this.source.sourceType; + } + + getSource() { + throw new Error("getSource() not implemented"); + } + + sendMessage(message) { + console.error(`Unknown source type: ${this.source.sourceType}`, message); + return FeedbackMessageType.ConsoleError; + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Commands/Commands.js b/packs/BP/scripts/classes/Commands/Commands.js new file mode 100644 index 0000000..4f38e54 --- /dev/null +++ b/packs/BP/scripts/classes/Commands/Commands.js @@ -0,0 +1,15 @@ +export class Commands { + static #commands = []; + + static register(command) { + this.#commands.push(command); + } + + static getAll() { + return [...this.#commands]; + } + + static clear() { + this.#commands = []; + } +} diff --git a/packs/BP/scripts/classes/Commands/EntityCommandOrigin.js b/packs/BP/scripts/classes/Commands/EntityCommandOrigin.js new file mode 100644 index 0000000..44f03b7 --- /dev/null +++ b/packs/BP/scripts/classes/Commands/EntityCommandOrigin.js @@ -0,0 +1,13 @@ +import { CommandOrigin } from "./CommandOrigin"; +import { FeedbackMessageType } from "./FeedbackMessageType"; + +export class EntityCommandOrigin extends CommandOrigin { + getSource() { + return this.source.sourceEntity; + } + + sendMessage(message) { + this.getSource().sendMessage(message); + return FeedbackMessageType.ChatMessage; + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Commands/FeedbackMessageType.js b/packs/BP/scripts/classes/Commands/FeedbackMessageType.js new file mode 100644 index 0000000..df683b6 --- /dev/null +++ b/packs/BP/scripts/classes/Commands/FeedbackMessageType.js @@ -0,0 +1,7 @@ +export const FeedbackMessageType = Object.freeze({ + None: "none", + ConsoleInfo: "info", + ConsoleWarn: "warn", + ConsoleError: "error", + ChatMessage: "message" +}); \ No newline at end of file diff --git a/packs/BP/scripts/classes/Commands/PlayerCommandOrigin.js b/packs/BP/scripts/classes/Commands/PlayerCommandOrigin.js new file mode 100644 index 0000000..f533ba5 --- /dev/null +++ b/packs/BP/scripts/classes/Commands/PlayerCommandOrigin.js @@ -0,0 +1,17 @@ +import { CommandOrigin } from "./CommandOrigin"; +import { FeedbackMessageType } from "./FeedbackMessageType"; + +export class PlayerCommandOrigin extends CommandOrigin { + getType() { + return "Player"; + } + + getSource() { + return this.source.sourceEntity; + } + + sendMessage(message) { + this.getSource().sendMessage(message); + return FeedbackMessageType.ChatMessage; + } +} \ No newline at end of file diff --git a/packs/BP/scripts/classes/Commands/ServerCommandOrigin.js b/packs/BP/scripts/classes/Commands/ServerCommandOrigin.js new file mode 100644 index 0000000..b6f322a --- /dev/null +++ b/packs/BP/scripts/classes/Commands/ServerCommandOrigin.js @@ -0,0 +1,13 @@ +import { CommandOrigin } from "./CommandOrigin"; +import { FeedbackMessageType } from "./FeedbackMessageType"; + +export class ServerCommandOrigin extends CommandOrigin { + getSource() { + return this.source.sourceType; + } + + sendMessage(message) { + console.log(message); + return FeedbackMessageType.ConsoleInfo; + } +} \ No newline at end of file