/**
* 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