/** * Author: dkanus * Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore * License: GPL * Copyright 2021-2023 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * * Acedia is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License, or * (at your option) any later version. * * Acedia is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Acedia. If not, see . */ class Command extends AcediaObject dependson(BaseText); //! This class is meant to represent a command type. //! //! Command class provides an automated way to add a command to a server through //! AcediaCore's features. It takes care of: //! //! 1. Verifying that player has passed correct (expected parameters); //! 2. Parsing these parameters into usable values (both standard, built-in //! types like `bool`, `int`, `float`, etc. and more advanced types such //! as players lists and JSON values); //! 3. Allowing you to easily specify a set of players you are targeting by //! supporting several ways to refer to them, such as *by name*, *by id* //! and *by selector* (@ and @self refer to caller player, @all refers //! to all players). //! 4. It can be registered inside AcediaCore's commands feature and be //! automatically called through the unified system that supports *chat* //! and *mutate* inputs (as well as allowing you to hook in any other //! input source); //! 5. Will also automatically provide a help page through built-in "help" //! command; //! 6. Subcommand support - when one command can have several distinct //! functions, depending on how its called (e.g. "inventory add" vs //! "inventory remove"). These subcommands have a special treatment in //! help pages, which makes them more preferable, compared to simply //! matching first `Text` argument; //! 7. Add support for "options" - additional flags that can modify commands //! behavior and behave like usual command options "--force"/"-f". //! Their short versions can even be combined: //! "give@ $ebr --ammo --force" can be rewritten as "give@ $ebr -af". //! And they can have their own parameters: "give@all --list sharp". //! //! # Implementation //! //! The idea of `Command`'s implementation is simple: command is basically the //! `Command.Data` struct that is filled via `CommandDataBuilder`. //! Whenever command is called it uses `CommandParser` to parse user's input //! based on its `Command.Data` and either report error (in case of failure) or //! pass make `Executed()`/`ExecutedFor()` calls (in case of success). //! //! When command is called is decided by `Commands_Feature` that tracks possible //! user inputs (and provides `HandleInput()`/`HandleInputWith()` methods for //! adding custom command inputs). That feature basically parses first part of //! the command: its name (not the subcommand's names) and target players //! (using `PlayersParser`, but only if command is targeted). //! //! Majority of the command-related code either serves to build `Command.Data` //! or to parse command input by using it (`CommandParser`). /// Possible errors that can arise when parsing command parameters from user /// input enum ErrorType { /// No error CET_None, /// Bad parser was provided to parse user input (this should not be possible) CET_BadParser, /// Sub-command name was not specified or was incorrect /// (this should not be possible) CET_NoSubCommands, /// Specified sub-command does not exist /// (only relevant when it is enforced for parser, e.g. by an alias) CET_BadSubCommand, /// Required param for command / option was not specified CET_NoRequiredParam, CET_NoRequiredParamForOption, /// Unknown option key was specified CET_UnknownOption, /// Unknown short option key was specified CET_UnknownShortOption, /// Same option appeared twice in one command call CET_RepeatedOption, /// Part of user's input could not be interpreted as a part of /// command's call CET_UnusedCommandParameters, /// In one short option specification (e.g. '-lah') several options require /// parameters: this introduces ambiguity and is not allowed CET_MultipleOptionsWithParams, /// Targets are specified incorrectly (for targeted commands only) CET_IncorrectTargetList, // No targets are specified (for targeted commands only) CET_EmptyTargetList }; /// Structure that contains all the information about how `Command` was called. struct CallData { /// Targeted players (if applicable) var public array targetPlayers; /// Specified sub-command and parameters/options var public Text subCommandName; /// Provided parameters and specified options var public HashTable parameters; var public HashTable options; /// Errors that occurred during command call processing are described by /// error type. var public ErrorType parsingError; /// Optional error textual name of the object (parameter, option, etc.) /// that caused it. var public Text errorCause; }; /// Possible types of parameters. enum ParameterType { /// Parses into `BoolBox` CPT_Boolean, /// Parses into `IntBox` CPT_Integer, /// Parses into `FloatBox` CPT_Number, /// Parses into `Text` CPT_Text, /// Special parameter that consumes the rest of the input into `Text` CPT_Remainder, /// Parses into `HashTable` CPT_Object, /// Parses into `ArrayList` CPT_Array, /// Parses into any JSON value CPT_JSON, /// Parses into an array of specified players CPT_Players }; /// Possible forms a boolean variable can be used as. /// Boolean parameter can define it's preferred format, which will be used for /// help page generation. enum PreferredBooleanFormat { PBF_TrueFalse, PBF_EnableDisable, PBF_OnOff, PBF_YesNo }; // Defines a singular command parameter struct Parameter { /// Display name (for the needs of help page displaying) var Text displayName; /// Type of value this parameter would store var ParameterType type; /// Does it take only a singular value or can it contain several of them, /// written in a list var bool allowsList; /// Variable name that will be used as a key to store parameter's value var Text variableName; /// (For `CPT_Boolean` type variables only) - preferred boolean format, /// used in help pages var PreferredBooleanFormat booleanFormat; /// `CPT_Text` can be attempted to be auto-resolved as an alias from some /// source during parsing. /// For command to attempt that, this field must be not-`none` and contain /// the name of the alias source (either "weapon", "color", "feature", /// "entity" or some kind of custom alias source name). /// /// Only relevant when given value is prefixed with "$" character. var Text aliasSourceName; }; /// Defines a sub-command of a this command /// (specified as " "). /// /// Using sub-command is not optional, but if none defined /// (in `BuildData()`) / specified by the player - an empty (`name.IsEmpty()`) /// one is automatically created / used. struct SubCommand { /// Name of the sub command. Cannot be `none`. var Text name; /// Human-readable description of the subcommand. Can be `none`. var Text description; /// List of required parameters of this [`Command`]. var array required; /// List of optional parameters of this [`Command`]. var array optional; }; /// Defines command's option (options are specified by "--long" or "-l"). /// Options are independent from sub-commands. struct Option { /// [`Option`]'s short name, i.e. a single letter "f" that can be specified /// in, e.g. "-laf" type option listings var BaseText.Character shortName; /// [`Option`]'s full name, e.g. "--force". var Text longName; /// Human-readable description of the option. Can be `none`. var Text description; /// List of required parameters of this [`Command::Option`]. var array required; /// List of required parameters of this [`Command::Option`]. var array optional; }; /// Structure that defines what sub-commands and options command has /// (and what parameters they take) struct Data { /// Command group this command belongs to var protected Text group; /// Short summary of what command does (recommended to /// keep it to 80 characters) var protected Text summary; /// Available subcommands. var protected array subCommands; /// Available options, common to all subcommands. var protected array