|
|
@ -1,12 +1,8 @@ |
|
|
|
/** |
|
|
|
/** |
|
|
|
* This class is meant to represent a command type: to create new command |
|
|
|
* Author: dkanus |
|
|
|
* one should extend it, then simply define required sub-commands/options and |
|
|
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore |
|
|
|
* parameters in `BuildData()` and overload `Executed()` / `ExecutedFor()` |
|
|
|
* License: GPL |
|
|
|
* to perform required actions when command is executed by a player. |
|
|
|
* Copyright 2021-2023 Anton Tarasenko |
|
|
|
* `Executed()` is called first, whenever command is executed and |
|
|
|
|
|
|
|
* `ExecuteFor()` is called only for targeted commands, once for each |
|
|
|
|
|
|
|
* targeted player. |
|
|
|
|
|
|
|
* Copyright 2021-2022 Anton Tarasenko |
|
|
|
|
|
|
|
*------------------------------------------------------------------------------ |
|
|
|
*------------------------------------------------------------------------------ |
|
|
|
* This file is part of Acedia. |
|
|
|
* This file is part of Acedia. |
|
|
|
* |
|
|
|
* |
|
|
@ -26,277 +22,234 @@ |
|
|
|
class Command extends AcediaObject |
|
|
|
class Command extends AcediaObject |
|
|
|
dependson(BaseText); |
|
|
|
dependson(BaseText); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
//! This class is meant to represent a command type. |
|
|
|
* # `Command` |
|
|
|
//! |
|
|
|
* |
|
|
|
//! Command class provides an automated way to add a command to a server through |
|
|
|
* Command class provides an automated way to add a command to a server through |
|
|
|
//! AcediaCore's features. It takes care of: |
|
|
|
* AcediaCore's features. It takes care of: |
|
|
|
//! |
|
|
|
* |
|
|
|
//! 1. Verifying that player has passed correct (expected parameters); |
|
|
|
* 1. Verifying that player has passed correct (expected parameters); |
|
|
|
//! 2. Parsing these parameters into usable values (both standard, built-in |
|
|
|
* 2. Parsing these parameters into usable values (both standard, built-in |
|
|
|
//! types like `bool`, `int`, `float`, etc. and more advanced types such |
|
|
|
* types like `bool`, `int`, `float`, etc. and more advanced types such |
|
|
|
//! as players lists and JSON values); |
|
|
|
* as players lists and JSON values); |
|
|
|
//! 3. Allowing you to easily specify a set of players you are targeting by |
|
|
|
* 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* |
|
|
|
* supporting several ways to refer to them, such as *by name*, *by id* |
|
|
|
//! and *by selector* (@ and @self refer to caller player, @all refers |
|
|
|
* and *by selector* (@ and @self refer to caller player, @all refers |
|
|
|
//! to all players). |
|
|
|
* to all players). |
|
|
|
//! 4. It can be registered inside AcediaCore's commands feature and be |
|
|
|
* 4. It can be registered inside AcediaCore's commands feature and be |
|
|
|
//! automatically called through the unified system that supports *chat* |
|
|
|
* automatically called through the unified system that supports *chat* |
|
|
|
//! and *mutate* inputs (as well as allowing you to hook in any other |
|
|
|
* and *mutate* inputs (as well as allowing you to hook in any other |
|
|
|
//! input source); |
|
|
|
* input source); |
|
|
|
//! 5. Will also automatically provide a help page through built-in "help" |
|
|
|
* 5. Will also automatically provide a help page through built-in "help" |
|
|
|
//! command; |
|
|
|
* command; |
|
|
|
//! 6. Subcommand support - when one command can have several distinct |
|
|
|
* 6. Subcommand support - when one command can have several distinct |
|
|
|
//! functions, depending on how its called (e.g. "inventory add" vs |
|
|
|
* functions, depending on how its called (e.g. "inventory add" vs |
|
|
|
//! "inventory remove"). These subcommands have a special treatment in |
|
|
|
* "inventory remove"). These subcommands have a special treatment in |
|
|
|
//! help pages, which makes them more preferable, compared to simply |
|
|
|
* help pages, which makes them more preferable, compared to simply |
|
|
|
//! matching first `Text` argument; |
|
|
|
* matching first `Text` argument; |
|
|
|
//! 7. Add support for "options" - additional flags that can modify commands |
|
|
|
* 7. Add support for "options" - additional flags that can modify commands |
|
|
|
//! behavior and behave like usual command options "--force"/"-f". |
|
|
|
* behavior and behave like usual command options "--force"/"-f". |
|
|
|
//! Their short versions can even be combined: |
|
|
|
* Their short versions can even be combined: |
|
|
|
//! "give@ $ebr --ammo --force" can be rewritten as "give@ $ebr -af". |
|
|
|
* "give@ $ebr --ammo --force" can be rewritten as "give@ $ebr -af". |
|
|
|
//! And they can have their own parameters: "give@all --list sharp". |
|
|
|
* And they can have their own parameters: "give@all --list sharp". |
|
|
|
//! |
|
|
|
* |
|
|
|
//! # Implementation |
|
|
|
* ## Usage |
|
|
|
//! |
|
|
|
* |
|
|
|
//! The idea of `Command`'s implementation is simple: command is basically the `Command.Data` struct |
|
|
|
* To create a custom command you need to simply: |
|
|
|
//! that is filled via `CommandDataBuilder`. |
|
|
|
* |
|
|
|
//! Whenever command is called it uses `CommandParser` to parse user's input based on its |
|
|
|
* 1. Create a custom command class derived from `Command`; |
|
|
|
//! `Command.Data` and either report error (in case of failure) or pass make |
|
|
|
* 2. Define `BuildData()` function and use given `CommandDataBuilder` to |
|
|
|
//! `Executed()`/`ExecutedFor()` calls (in case of success). |
|
|
|
* fill-in data about what parameters your command takes. You can also |
|
|
|
//! |
|
|
|
* add optional descriptions that would appear in your command's |
|
|
|
//! When command is called is decided by `Commands_Feature` that tracks possible user inputs |
|
|
|
* help page. |
|
|
|
//! (and provides `HandleInput()`/`HandleInputWith()` methods for adding custom command inputs). |
|
|
|
* 3. Overload `Executed()` or `ExecutedFor()` (or both) method and add |
|
|
|
//! That feature basically parses first part of the command: its name (not the subcommand's names) |
|
|
|
* whatever logic you want to execute once your command was called. |
|
|
|
//! and target players (using `PlayersParser`, but only if command is targeted). |
|
|
|
* All parameters and options will be listed inside passed `CallData` |
|
|
|
//! |
|
|
|
* parameter. These methods will only be called if all necessary |
|
|
|
//! Majority of the command-related code either serves to build `Command.Data` or to parse command |
|
|
|
* parameters were correctly specified. |
|
|
|
//! input by using it (`CommandParser`). |
|
|
|
* |
|
|
|
|
|
|
|
* ## Implementation |
|
|
|
/// Possible errors that can arise when parsing command parameters from user input |
|
|
|
* |
|
|
|
enum ErrorType { |
|
|
|
* The idea of `Command`'s implementation is simple: command is basically |
|
|
|
// No error |
|
|
|
* 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, |
|
|
|
CET_None, |
|
|
|
// Bad parser was provided to parse user input |
|
|
|
// Bad parser was provided to parse user input (this should not be possible) |
|
|
|
// (this should not be possible) |
|
|
|
|
|
|
|
CET_BadParser, |
|
|
|
CET_BadParser, |
|
|
|
// Sub-command name was not specified or was incorrect |
|
|
|
// Sub-command name was not specified or was incorrect |
|
|
|
// (this should not be possible) |
|
|
|
// (this should not be possible) |
|
|
|
CET_NoSubCommands, |
|
|
|
CET_NoSubCommands, |
|
|
|
// Specified sub-command does not exist |
|
|
|
// Specified sub-command does not exist |
|
|
|
// (only relevant when it is enforced for parser, e.g. by an alias) |
|
|
|
// (only relevant when it is enforced for parser, e.g. by an alias) |
|
|
|
CET_BadSubCommand, |
|
|
|
CET_BadSubCommand, |
|
|
|
// Required param for command / option was not specified |
|
|
|
// Required param for command / option was not specified |
|
|
|
CET_NoRequiredParam, |
|
|
|
CET_NoRequiredParam, |
|
|
|
CET_NoRequiredParamForOption, |
|
|
|
CET_NoRequiredParamForOption, |
|
|
|
// Unknown option key was specified |
|
|
|
// Unknown option key was specified |
|
|
|
CET_UnknownOption, |
|
|
|
CET_UnknownOption, |
|
|
|
CET_UnknownShortOption, |
|
|
|
CET_UnknownShortOption, |
|
|
|
// Same option appeared twice in one command call |
|
|
|
// Same option appeared twice in one command call |
|
|
|
CET_RepeatedOption, |
|
|
|
CET_RepeatedOption, |
|
|
|
// Part of user's input could not be interpreted as a part of |
|
|
|
// Part of user's input could not be interpreted as a part of |
|
|
|
// command's call |
|
|
|
// command's call |
|
|
|
CET_UnusedCommandParameters, |
|
|
|
CET_UnusedCommandParameters, |
|
|
|
// In one short option specification (e.g. '-lah') several options |
|
|
|
// In one short option specification (e.g. '-lah') several options require parameters: |
|
|
|
// require parameters: this introduces ambiguity and is not allowed |
|
|
|
// this introduces ambiguity and is not allowed |
|
|
|
CET_MultipleOptionsWithParams, |
|
|
|
CET_MultipleOptionsWithParams, |
|
|
|
// (For targeted commands only) |
|
|
|
// (For targeted commands only) |
|
|
|
// Targets are specified incorrectly (or none actually specified) |
|
|
|
// Targets are specified incorrectly (or none actually specified) |
|
|
|
CET_IncorrectTargetList, |
|
|
|
CET_IncorrectTargetList, |
|
|
|
CET_EmptyTargetList |
|
|
|
CET_EmptyTargetList |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Structure that contains all the information about how `Command` was called. |
|
|
|
* Structure that contains all the information about how `Command` was called. |
|
|
|
struct CallData { |
|
|
|
*/ |
|
|
|
// Targeted players (if applicable) |
|
|
|
struct CallData |
|
|
|
var public array<EPlayer> targetPlayers; |
|
|
|
{ |
|
|
|
// Specified sub-command and parameters/options |
|
|
|
// Targeted players (if applicable) |
|
|
|
var public Text subCommandName; |
|
|
|
var public array<EPlayer> targetPlayers; |
|
|
|
// Provided parameters and specified options |
|
|
|
// Specified sub-command and parameters/options |
|
|
|
var public HashTable parameters; |
|
|
|
var public Text subCommandName; |
|
|
|
var public HashTable options; |
|
|
|
// Provided parameters and specified options |
|
|
|
// Errors that occurred during command call processing are described by |
|
|
|
var public HashTable parameters; |
|
|
|
// error type and optional error textual name of the object |
|
|
|
var public HashTable options; |
|
|
|
// (parameter, option, etc.) that caused it. |
|
|
|
// Errors that occurred during command call processing are described by |
|
|
|
var public ErrorType parsingError; |
|
|
|
// error type and optional error textual name of the object |
|
|
|
var public Text errorCause; |
|
|
|
// (parameter, option, etc.) that caused it. |
|
|
|
|
|
|
|
var public ErrorType parsingError; |
|
|
|
|
|
|
|
var public Text errorCause; |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Possible types of parameters. |
|
|
|
* Possible types of parameters. |
|
|
|
enum ParameterType { |
|
|
|
*/ |
|
|
|
// Parses into `BoolBox` |
|
|
|
enum ParameterType |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Parses into `BoolBox` |
|
|
|
|
|
|
|
CPT_Boolean, |
|
|
|
CPT_Boolean, |
|
|
|
// Parses into `IntBox` |
|
|
|
// Parses into `IntBox` |
|
|
|
CPT_Integer, |
|
|
|
CPT_Integer, |
|
|
|
// Parses into `FloatBox` |
|
|
|
// Parses into `FloatBox` |
|
|
|
CPT_Number, |
|
|
|
CPT_Number, |
|
|
|
// Parses into `Text` |
|
|
|
// Parses into `Text` |
|
|
|
CPT_Text, |
|
|
|
CPT_Text, |
|
|
|
// Special parameter that consumes the rest of the input into `Text` |
|
|
|
// Special parameter that consumes the rest of the input into `Text` |
|
|
|
CPT_Remainder, |
|
|
|
CPT_Remainder, |
|
|
|
// Parses into `HashTable` |
|
|
|
// Parses into `HashTable` |
|
|
|
CPT_Object, |
|
|
|
CPT_Object, |
|
|
|
// Parses into `ArrayList` |
|
|
|
// Parses into `ArrayList` |
|
|
|
CPT_Array, |
|
|
|
CPT_Array, |
|
|
|
// Parses into any JSON value |
|
|
|
// Parses into any JSON value |
|
|
|
CPT_JSON, |
|
|
|
CPT_JSON, |
|
|
|
// Parses into an array of specified players |
|
|
|
// Parses into an array of specified players |
|
|
|
CPT_Players |
|
|
|
CPT_Players |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Possible forms a boolean variable can be used as. |
|
|
|
* 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. |
|
|
|
* Boolean parameter can define it's preferred format, which will be used |
|
|
|
enum PreferredBooleanFormat { |
|
|
|
* for help page generation. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
enum PreferredBooleanFormat |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
PBF_TrueFalse, |
|
|
|
PBF_TrueFalse, |
|
|
|
PBF_EnableDisable, |
|
|
|
PBF_EnableDisable, |
|
|
|
PBF_OnOff, |
|
|
|
PBF_OnOff, |
|
|
|
PBF_YesNo |
|
|
|
PBF_YesNo |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Defines a singular command parameter |
|
|
|
// Defines a singular command parameter |
|
|
|
struct Parameter |
|
|
|
struct Parameter { |
|
|
|
{ |
|
|
|
// Display name (for the needs of help page displaying) |
|
|
|
// Display name (for the needs of help page displaying) |
|
|
|
var Text displayName; |
|
|
|
var Text displayName; |
|
|
|
// Type of value this parameter would store |
|
|
|
// Type of value this parameter would store |
|
|
|
var ParameterType type; |
|
|
|
var ParameterType type; |
|
|
|
// Does it take only a singular value or can it contain several of them, |
|
|
|
// Does it take only a singular value or can it contain several of them, |
|
|
|
// written in a list |
|
|
|
// written in a list |
|
|
|
var bool allowsList; |
|
|
|
var bool allowsList; |
|
|
|
// Variable name that will be used as a key to store parameter's value |
|
|
|
// Variable name that will be used as a key to store parameter's value |
|
|
|
var Text variableName; |
|
|
|
var Text variableName; |
|
|
|
// (For `CPT_Boolean` type variables only) - preferred boolean format, |
|
|
|
// (For `CPT_Boolean` type variables only) - preferred boolean format, |
|
|
|
// used in help pages |
|
|
|
// used in help pages |
|
|
|
|
|
|
|
var PreferredBooleanFormat booleanFormat; |
|
|
|
var PreferredBooleanFormat booleanFormat; |
|
|
|
// `CPT_Text` can be attempted to be auto-resolved as an alias from |
|
|
|
// `CPT_Text` can be attempted to be auto-resolved as an alias from some source during parsing. |
|
|
|
/// some source during parsing. For command to attempt that, this field must |
|
|
|
// For command to attempt that, this field must be not-`none` and contain the name of |
|
|
|
// be not-`none` and contain the name of the alias source (either "weapon", |
|
|
|
// the alias source (either "weapon", "color", "feature", "entity" or some kind of custom alias |
|
|
|
// "color", "feature", "entity" or some kind of custom alias source name). |
|
|
|
// source name). |
|
|
|
// Only relevant when given value is prefixed with "$" character. |
|
|
|
// |
|
|
|
var Text aliasSourceName; |
|
|
|
// Only relevant when given value is prefixed with "$" character. |
|
|
|
|
|
|
|
var Text aliasSourceName; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Defines a sub-command of a this command (specified as |
|
|
|
// Defines a sub-command of a this command (specified as "<command> <sub_command>"). |
|
|
|
// "<command> <sub_command>"). |
|
|
|
// |
|
|
|
// Using sub-command is not optional, but if none defined |
|
|
|
// Using sub-command is not optional, but if none defined (in `BuildData()`) / specified by |
|
|
|
// (in `BuildData()`) / specified by the player - an empty (`name.IsEmpty()`) |
|
|
|
// the player - an empty (`name.IsEmpty()`) one is automatically created / used. |
|
|
|
// one is automatically created / used. |
|
|
|
struct SubCommand { |
|
|
|
struct SubCommand |
|
|
|
// Cannot be `none` |
|
|
|
{ |
|
|
|
var Text name; |
|
|
|
// Cannot be `none` |
|
|
|
// Can be `none` |
|
|
|
var Text name; |
|
|
|
|
|
|
|
// Can be `none` |
|
|
|
|
|
|
|
var Text description; |
|
|
|
var Text description; |
|
|
|
var array<Parameter> required; |
|
|
|
var array<Parameter> required; |
|
|
|
var array<Parameter> optional; |
|
|
|
var array<Parameter> optional; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Defines command's option (options are specified by "--long" or "-l"). |
|
|
|
// Defines command's option (options are specified by "--long" or "-l"). |
|
|
|
// Options are independent from sub-commands. |
|
|
|
// Options are independent from sub-commands. |
|
|
|
struct Option |
|
|
|
struct Option { |
|
|
|
{ |
|
|
|
var BaseText.Character shortName; |
|
|
|
var BaseText.Character shortName; |
|
|
|
var Text longName; |
|
|
|
var Text longName; |
|
|
|
var Text description; |
|
|
|
var Text description; |
|
|
|
// Option can also have their own parameters |
|
|
|
// Option can also have their own parameters |
|
|
|
var array<Parameter> required; |
|
|
|
var array<Parameter> required; |
|
|
|
var array<Parameter> optional; |
|
|
|
var array<Parameter> optional; |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Structure that defines what sub-commands and options command has |
|
|
|
// Structure that defines what sub-commands and options command has |
|
|
|
// (and what parameters they take) |
|
|
|
// (and what parameters they take) |
|
|
|
struct Data |
|
|
|
struct Data { |
|
|
|
{ |
|
|
|
// Default command name that will be used unless Acedia is configured to |
|
|
|
// Default command name that will be used unless Acedia is configured to |
|
|
|
// do otherwise |
|
|
|
// do otherwise |
|
|
|
var protected Text name; |
|
|
|
var protected Text name; |
|
|
|
// Command group this command belongs to |
|
|
|
// Command group this command belongs to |
|
|
|
var protected Text group; |
|
|
|
var protected Text group; |
|
|
|
// Short summary of what command does (recommended to |
|
|
|
// Short summary of what command does (recommended to |
|
|
|
// keep it to 80 characters) |
|
|
|
// keep it to 80 characters) |
|
|
|
var protected Text summary; |
|
|
|
var protected Text summary; |
|
|
|
|
|
|
|
var protected array<SubCommand> subCommands; |
|
|
|
var protected array<SubCommand> subCommands; |
|
|
|
var protected array<Option> options; |
|
|
|
var protected array<Option> options; |
|
|
|
var protected bool requiresTarget; |
|
|
|
var protected bool requiresTarget; |
|
|
|
}; |
|
|
|
}; |
|
|
|
var private Data commandData; |
|
|
|
var private Data commandData; |
|
|
|
|
|
|
|
|
|
|
|
// We do not really ever need to create more than one instance of each class |
|
|
|
// We do not really ever need to create more than one instance of each class |
|
|
|
// of `Command`, so we will simply store and reuse one created instance. |
|
|
|
// of `Command`, so we will simply store and reuse one created instance. |
|
|
|
var private Command mainInstance; |
|
|
|
var private Command mainInstance; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* When command is being executed we create several instances of |
|
|
|
// When command is being executed we create several instances of `ConsoleWriter` that can be used |
|
|
|
* `ConsoleWriter` that can be used for command output. They will also be |
|
|
|
// for command output. They will also be automatically deallocated once command is executed. |
|
|
|
* automatically deallocated once command is executed. |
|
|
|
// |
|
|
|
* DO NOT modify them or deallocate any of them manually. |
|
|
|
// DO NOT modify them or deallocate any of them manually. |
|
|
|
* This should make output more convenient and standardized. |
|
|
|
// |
|
|
|
* |
|
|
|
// This should make output more convenient and standardized. |
|
|
|
* 1. `publicConsole` - sends messages to all present players; |
|
|
|
// |
|
|
|
* 2. `callerConsole` - sends messages to the player that |
|
|
|
// 1. `publicConsole` - sends messages to all present players; |
|
|
|
* called the command; |
|
|
|
// 2. `callerConsole` - sends messages to the player that called the command; |
|
|
|
* 3. `targetConsole` - sends messages to the player that is currently |
|
|
|
// 3. `targetConsole` - sends messages to the player that is currently being targeted |
|
|
|
* being targeted (different each call of `ExecutedFor()` and |
|
|
|
// (different each call of `ExecutedFor()` and `none` during `Executed()` call); |
|
|
|
* `none` during `Executed()` call); |
|
|
|
// 4. `othersConsole` - sends messaged to every player that is neither "caller" or "target". |
|
|
|
* 4. `othersConsole` - sends messaged to every player that is |
|
|
|
|
|
|
|
* neither "caller" or "target". |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
var protected ConsoleWriter publicConsole, othersConsole; |
|
|
|
var protected ConsoleWriter publicConsole, othersConsole; |
|
|
|
var protected ConsoleWriter callerConsole, targetConsole; |
|
|
|
var protected ConsoleWriter callerConsole, targetConsole; |
|
|
|
|
|
|
|
|
|
|
|
protected function Constructor() |
|
|
|
protected function Constructor() { |
|
|
|
{ |
|
|
|
|
|
|
|
local CommandDataBuilder dataBuilder; |
|
|
|
local CommandDataBuilder dataBuilder; |
|
|
|
dataBuilder = |
|
|
|
dataBuilder = CommandDataBuilder(_.memory.Allocate(class'CommandDataBuilder')); |
|
|
|
CommandDataBuilder(_.memory.Allocate(class'CommandDataBuilder')); |
|
|
|
|
|
|
|
BuildData(dataBuilder); |
|
|
|
BuildData(dataBuilder); |
|
|
|
commandData = dataBuilder.BorrowData(); |
|
|
|
commandData = dataBuilder.BorrowData(); |
|
|
|
dataBuilder.FreeSelf(); |
|
|
|
dataBuilder.FreeSelf(); |
|
|
|
dataBuilder = none; |
|
|
|
dataBuilder = none; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected function Finalizer() |
|
|
|
protected function Finalizer() { |
|
|
|
{ |
|
|
|
local int i; |
|
|
|
local int i; |
|
|
|
|
|
|
|
local array<SubCommand> subCommands; |
|
|
|
local array<SubCommand> subCommands; |
|
|
|
local array<Option> options; |
|
|
|
local array<Option> options; |
|
|
|
|
|
|
|
|
|
|
|
DeallocateConsoles(); |
|
|
|
DeallocateConsoles(); |
|
|
|
_.memory.Free(commandData.name); |
|
|
|
_.memory.Free(commandData.name); |
|
|
|
_.memory.Free(commandData.summary); |
|
|
|
_.memory.Free(commandData.summary); |
|
|
|
subCommands = commandData.subCommands; |
|
|
|
subCommands = commandData.subCommands; |
|
|
|
for (i = 0; i < options.length; i += 1) |
|
|
|
for (i = 0; i < options.length; i += 1) { |
|
|
|
{ |
|
|
|
|
|
|
|
_.memory.Free(subCommands[i].name); |
|
|
|
_.memory.Free(subCommands[i].name); |
|
|
|
_.memory.Free(subCommands[i].description); |
|
|
|
_.memory.Free(subCommands[i].description); |
|
|
|
CleanParameters(subCommands[i].required); |
|
|
|
CleanParameters(subCommands[i].required); |
|
|
@ -306,8 +259,7 @@ protected function Finalizer() |
|
|
|
} |
|
|
|
} |
|
|
|
commandData.subCommands.length = 0; |
|
|
|
commandData.subCommands.length = 0; |
|
|
|
options = commandData.options; |
|
|
|
options = commandData.options; |
|
|
|
for (i = 0; i < options.length; i += 1) |
|
|
|
for (i = 0; i < options.length; i += 1) { |
|
|
|
{ |
|
|
|
|
|
|
|
_.memory.Free(options[i].longName); |
|
|
|
_.memory.Free(options[i].longName); |
|
|
|
_.memory.Free(options[i].description); |
|
|
|
_.memory.Free(options[i].description); |
|
|
|
CleanParameters(options[i].required); |
|
|
|
CleanParameters(options[i].required); |
|
|
@ -318,115 +270,88 @@ protected function Finalizer() |
|
|
|
commandData.options.length = 0; |
|
|
|
commandData.options.length = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private final function CleanParameters(array<Parameter> parameters) |
|
|
|
private final function CleanParameters(array<Parameter> parameters) { |
|
|
|
{ |
|
|
|
|
|
|
|
local int i; |
|
|
|
local int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < parameters.length; i += 1) |
|
|
|
for (i = 0; i < parameters.length; i += 1) { |
|
|
|
{ |
|
|
|
|
|
|
|
_.memory.Free(parameters[i].displayName); |
|
|
|
_.memory.Free(parameters[i].displayName); |
|
|
|
_.memory.Free(parameters[i].variableName); |
|
|
|
_.memory.Free(parameters[i].variableName); |
|
|
|
_.memory.Free(parameters[i].aliasSourceName); |
|
|
|
_.memory.Free(parameters[i].aliasSourceName); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Overload this method to use `builder` to define parameters and options for your command. |
|
|
|
* Overload this method to use `builder` to define parameters and options for |
|
|
|
|
|
|
|
* your command. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param builder Builder that can be used to define your commands parameters |
|
|
|
|
|
|
|
* and options. Do not deallocate. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected function BuildData(CommandDataBuilder builder){} |
|
|
|
protected function BuildData(CommandDataBuilder builder){} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Overload this method to perform required actions when your command is called. |
|
|
|
* Overload this method to perform required actions when |
|
|
|
/// |
|
|
|
* your command is called. |
|
|
|
/// [`arguments`] is a `struct` filled with parameters that your command has been called with. |
|
|
|
* |
|
|
|
/// Guaranteed to not be in error state. |
|
|
|
* @param arguments `struct` filled with parameters that your command |
|
|
|
/// |
|
|
|
* has been called with. Guaranteed to not be in error state. |
|
|
|
/// [`instigator`] is a player that instigated this execution. |
|
|
|
* @param instigator Player that instigated this execution. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected function Executed(CallData arguments, EPlayer instigator){} |
|
|
|
protected function Executed(CallData arguments, EPlayer instigator){} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Overload this method to perform required actions when your command is called with a given player |
|
|
|
* Overload this method to perform required actions when your command is called |
|
|
|
/// as a target. |
|
|
|
* with a given player as a target. If several players have been specified - |
|
|
|
/// |
|
|
|
* this method will be called once for each. |
|
|
|
/// If several players have been specified - this method will be called once for each. |
|
|
|
* |
|
|
|
/// |
|
|
|
* If your command does not require a target - this method will not be called. |
|
|
|
/// If your command does not require a target - this method will not be called. |
|
|
|
* |
|
|
|
/// |
|
|
|
* @param target Player that this command must perform an action on. |
|
|
|
/// [`target`] is a player that this command must perform an action on. |
|
|
|
* @param arguments `struct` filled with parameters that your command |
|
|
|
/// [`arguments`] is a `struct` filled with parameters that your command has been called with. |
|
|
|
* has been called with. Guaranteed to not be in error state and contain |
|
|
|
/// Guaranteed to not be in error state. |
|
|
|
* all the required data. |
|
|
|
/// |
|
|
|
* @param instigator Player that instigated this call. |
|
|
|
/// [`instigator`] is a player that instigated this execution. |
|
|
|
*/ |
|
|
|
protected function ExecutedFor(EPlayer target, CallData arguments, EPlayer instigator) {} |
|
|
|
protected function ExecutedFor( |
|
|
|
|
|
|
|
EPlayer target, |
|
|
|
/// Returns an instance of command (of particular class) that is stored "as a singleton" in |
|
|
|
CallData arguments, |
|
|
|
/// command's class itself. Do not deallocate it. |
|
|
|
EPlayer instigator){} |
|
|
|
public final static function Command GetInstance() { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Returns an instance of command (of particular class) that is stored |
|
|
|
|
|
|
|
* "as a singleton" in command's class itself. Do not deallocate it. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final static function Command GetInstance() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (default.mainInstance == none) { |
|
|
|
if (default.mainInstance == none) { |
|
|
|
default.mainInstance = Command(__().memory.Allocate(default.class)); |
|
|
|
default.mainInstance = Command(__().memory.Allocate(default.class)); |
|
|
|
} |
|
|
|
} |
|
|
|
return default.mainInstance; |
|
|
|
return default.mainInstance; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Forces command to process (parse) player's input, producing a structure with parsed data in |
|
|
|
* Forces command to process (parse) player's input, producing a structure |
|
|
|
/// Acedia's format instead. |
|
|
|
* with parsed data in Acedia's format instead. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// Use `Execute()` for actually performing command's actions. |
|
|
|
* @see `Execute()` for actually performing command's actions. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// [`subCommandName`] can be optionally specified to use as sub-command. |
|
|
|
* @param parser Parser that contains command input. |
|
|
|
/// If this argument's value is `none` - sub-command name will be parsed from the `parser`'s data. |
|
|
|
* @param callerPlayer Player that initiated this command's call, |
|
|
|
/// |
|
|
|
* necessary for parsing player list (since it can point at |
|
|
|
/// Returns `CallData` structure that contains all the information about parameters specified in |
|
|
|
* the caller player). |
|
|
|
/// `parser`'s contents. |
|
|
|
* @param subCommandName This method can optionally specify sub-command to |
|
|
|
/// Returned structure contains objects that must be deallocated, which can easily be done by |
|
|
|
* caller command to use. If this argument's value is `none` - sub-command |
|
|
|
/// the auxiliary `DeallocateCallData()` method. |
|
|
|
* name will be parsed from the `parser`'s data. |
|
|
|
|
|
|
|
* @return `CallData` structure that contains all the information about |
|
|
|
|
|
|
|
* parameters specified in `parser`'s contents. |
|
|
|
|
|
|
|
* Returned structure contains objects that must be deallocated, |
|
|
|
|
|
|
|
* which can easily be done by the auxiliary `DeallocateCallData()` method. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final function CallData ParseInputWith( |
|
|
|
public final function CallData ParseInputWith( |
|
|
|
Parser parser, |
|
|
|
Parser parser, |
|
|
|
EPlayer callerPlayer, |
|
|
|
EPlayer callerPlayer, |
|
|
|
optional BaseText subCommandName) |
|
|
|
optional BaseText subCommandName |
|
|
|
{ |
|
|
|
) { |
|
|
|
local array<EPlayer> targetPlayers; |
|
|
|
local array<EPlayer> targetPlayers; |
|
|
|
local CommandParser commandParser; |
|
|
|
local CommandParser commandParser; |
|
|
|
local CallData callData; |
|
|
|
local CallData callData; |
|
|
|
|
|
|
|
|
|
|
|
if (parser == none || !parser.Ok()) |
|
|
|
if (parser == none || !parser.Ok()) { |
|
|
|
{ |
|
|
|
|
|
|
|
callData.parsingError = CET_BadParser; |
|
|
|
callData.parsingError = CET_BadParser; |
|
|
|
return callData; |
|
|
|
return callData; |
|
|
|
} |
|
|
|
} |
|
|
|
// Parse targets and handle errors that can arise here |
|
|
|
// Parse targets and handle errors that can arise here |
|
|
|
if (commandData.requiresTarget) |
|
|
|
if (commandData.requiresTarget) { |
|
|
|
{ |
|
|
|
|
|
|
|
targetPlayers = ParseTargets(parser, callerPlayer); |
|
|
|
targetPlayers = ParseTargets(parser, callerPlayer); |
|
|
|
if (!parser.Ok()) |
|
|
|
if (!parser.Ok()) { |
|
|
|
{ |
|
|
|
|
|
|
|
callData.parsingError = CET_IncorrectTargetList; |
|
|
|
callData.parsingError = CET_IncorrectTargetList; |
|
|
|
return callData; |
|
|
|
return callData; |
|
|
|
} |
|
|
|
} |
|
|
|
if (targetPlayers.length <= 0) |
|
|
|
if (targetPlayers.length <= 0) { |
|
|
|
{ |
|
|
|
|
|
|
|
callData.parsingError = CET_EmptyTargetList; |
|
|
|
callData.parsingError = CET_EmptyTargetList; |
|
|
|
return callData; |
|
|
|
return callData; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// Parse parameters themselves |
|
|
|
// Parse parameters themselves |
|
|
|
commandParser = CommandParser(_.memory.Allocate(class'CommandParser')); |
|
|
|
commandParser = CommandParser(_.memory.Allocate(class'CommandParser')); |
|
|
|
callData = commandParser.ParseWith( |
|
|
|
callData = commandParser.ParseWith( |
|
|
|
parser, |
|
|
|
parser, |
|
|
@ -438,33 +363,24 @@ public final function CallData ParseInputWith( |
|
|
|
return callData; |
|
|
|
return callData; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Executes caller `Command` with data provided by `callData` if it is in a correct state and |
|
|
|
* Executes caller `Command` with data provided by `callData` if it is in |
|
|
|
/// reports error to `callerPlayer` if `callData` is invalid. |
|
|
|
* a correct state and reports error to `callerPlayer` if |
|
|
|
/// |
|
|
|
* `callData` is invalid. |
|
|
|
/// Returns `true` if command was successfully executed and `false` otherwise. |
|
|
|
* |
|
|
|
/// Execution is considered successful if `Execute()` call was made, regardless of whether `Command` |
|
|
|
* @param callData Data about parameters, options, etc. with which |
|
|
|
/// can actually perform required action. |
|
|
|
* caller `Command` is to be executed. |
|
|
|
/// For example, giving a weapon to a player can fail because he does not have enough space in his |
|
|
|
* @param callerPlayer Player that should be considered responsible for |
|
|
|
/// inventory, but it will still be considered a successful execution as far as return value is |
|
|
|
* executing this `Command`. |
|
|
|
/// concerned. |
|
|
|
* @return `true` if command was successfully executed and `false` otherwise. |
|
|
|
public final function bool Execute(CallData callData, EPlayer callerPlayer) { |
|
|
|
* Execution is considered successful if `Execute()` call was made, |
|
|
|
local int i; |
|
|
|
* regardless of whether `Command` can actually perform required action. |
|
|
|
local array<EPlayer> targetPlayers; |
|
|
|
* For example, giving a weapon to a player can fail because he does not |
|
|
|
|
|
|
|
* have enough space in his inventory, but it will still be considered |
|
|
|
|
|
|
|
* a successful execution as far as return value is concerned. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final function bool Execute(CallData callData, EPlayer callerPlayer) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
local int i; |
|
|
|
|
|
|
|
local array<EPlayer> targetPlayers; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (callerPlayer == none) return false; |
|
|
|
if (callerPlayer == none) return false; |
|
|
|
if (!callerPlayer.IsExistent()) return false; |
|
|
|
if (!callerPlayer.IsExistent()) return false; |
|
|
|
|
|
|
|
|
|
|
|
// Report or execute |
|
|
|
// Report or execute |
|
|
|
if (callData.parsingError != CET_None) |
|
|
|
if (callData.parsingError != CET_None) { |
|
|
|
{ |
|
|
|
|
|
|
|
ReportError(callData, callerPlayer); |
|
|
|
ReportError(callData, callerPlayer); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
@ -475,14 +391,12 @@ public final function bool Execute(CallData callData, EPlayer callerPlayer) |
|
|
|
.Write(P("Executing command `")) |
|
|
|
.Write(P("Executing command `")) |
|
|
|
.Write(commandData.name) |
|
|
|
.Write(commandData.name) |
|
|
|
.Say(P("`")); |
|
|
|
.Say(P("`")); |
|
|
|
// `othersConsole` should also exist in time for `Executed()` call |
|
|
|
// `othersConsole` should also exist in time for `Executed()` call |
|
|
|
othersConsole = _.console.ForAll().ButPlayer(callerPlayer); |
|
|
|
othersConsole = _.console.ForAll().ButPlayer(callerPlayer); |
|
|
|
Executed(callData, callerPlayer); |
|
|
|
Executed(callData, callerPlayer); |
|
|
|
_.memory.Free(othersConsole); |
|
|
|
_.memory.Free(othersConsole); |
|
|
|
if (commandData.requiresTarget) |
|
|
|
if (commandData.requiresTarget) { |
|
|
|
{ |
|
|
|
for (i = 0; i < targetPlayers.length; i += 1) { |
|
|
|
for (i = 0; i < targetPlayers.length; i += 1) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
targetConsole = _.console.For(targetPlayers[i]); |
|
|
|
targetConsole = _.console.For(targetPlayers[i]); |
|
|
|
othersConsole = _.console |
|
|
|
othersConsole = _.console |
|
|
|
.ForAll() |
|
|
|
.ForAll() |
|
|
@ -499,8 +413,7 @@ public final function bool Execute(CallData callData, EPlayer callerPlayer) |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private final function DeallocateConsoles() |
|
|
|
private final function DeallocateConsoles() { |
|
|
|
{ |
|
|
|
|
|
|
|
if (publicConsole != none && publicConsole.IsAllocated()) { |
|
|
|
if (publicConsole != none && publicConsole.IsAllocated()) { |
|
|
|
_.memory.Free(publicConsole); |
|
|
|
_.memory.Free(publicConsole); |
|
|
|
} |
|
|
|
} |
|
|
@ -519,16 +432,8 @@ private final function DeallocateConsoles() |
|
|
|
othersConsole = none; |
|
|
|
othersConsole = none; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Auxiliary method that cleans up all data and deallocates all objects inside provided structure. |
|
|
|
* Auxiliary method that cleans up all data and deallocates all objects inside |
|
|
|
public final static function DeallocateCallData(/* take */ CallData callData) { |
|
|
|
* provided `callData` structure. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param callData Structure to clean. All stored data will be cleared, |
|
|
|
|
|
|
|
* meaning that `DeallocateCallData()` method takes ownership of |
|
|
|
|
|
|
|
* this parameter. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final static function DeallocateCallData(/* take */ CallData callData) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
__().memory.Free(callData.subCommandName); |
|
|
|
__().memory.Free(callData.subCommandName); |
|
|
|
__().memory.Free(callData.parameters); |
|
|
|
__().memory.Free(callData.parameters); |
|
|
|
__().memory.Free(callData.options); |
|
|
|
__().memory.Free(callData.options); |
|
|
@ -539,100 +444,100 @@ public final static function DeallocateCallData(/* take */ CallData callData) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Reports given error to the `callerPlayer`, appropriately picking |
|
|
|
// Reports given error to the `callerPlayer`, appropriately picking |
|
|
|
// message color |
|
|
|
// message color |
|
|
|
private final function ReportError(CallData callData, EPlayer callerPlayer) |
|
|
|
private final function ReportError(CallData callData, EPlayer callerPlayer) { |
|
|
|
{ |
|
|
|
local Text errorMessage; |
|
|
|
local Text errorMessage; |
|
|
|
|
|
|
|
local ConsoleWriter console; |
|
|
|
local ConsoleWriter console; |
|
|
|
|
|
|
|
|
|
|
|
if (callerPlayer == none) return; |
|
|
|
if (callerPlayer == none) return; |
|
|
|
if (!callerPlayer.IsExistent()) return; |
|
|
|
if (!callerPlayer.IsExistent()) return; |
|
|
|
|
|
|
|
|
|
|
|
// Setup console color |
|
|
|
// Setup console color |
|
|
|
console = callerPlayer.BorrowConsole(); |
|
|
|
console = callerPlayer.BorrowConsole(); |
|
|
|
if (callData.parsingError == CET_EmptyTargetList) { |
|
|
|
if (callData.parsingError == CET_EmptyTargetList) { |
|
|
|
console.UseColor(_.color.textWarning); |
|
|
|
console.UseColor(_.color.textWarning); |
|
|
|
} |
|
|
|
} else { |
|
|
|
else { |
|
|
|
|
|
|
|
console.UseColor(_.color.textFailure); |
|
|
|
console.UseColor(_.color.textFailure); |
|
|
|
} |
|
|
|
} |
|
|
|
// Send message |
|
|
|
// Send message |
|
|
|
errorMessage = PrintErrorMessage(callData); |
|
|
|
errorMessage = PrintErrorMessage(callData); |
|
|
|
console.Say(errorMessage); |
|
|
|
console.Say(errorMessage); |
|
|
|
errorMessage.FreeSelf(); |
|
|
|
errorMessage.FreeSelf(); |
|
|
|
// Restore console color |
|
|
|
// Restore console color |
|
|
|
console.ResetColor().Flush(); |
|
|
|
console.ResetColor().Flush(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private final function Text PrintErrorMessage(CallData callData) |
|
|
|
private final function Text PrintErrorMessage(CallData callData) { |
|
|
|
{ |
|
|
|
local Text result; |
|
|
|
local Text result; |
|
|
|
local MutableText builder; |
|
|
|
local MutableText builder; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
builder = _.text.Empty(); |
|
|
|
builder = _.text.Empty(); |
|
|
|
switch (callData.parsingError) |
|
|
|
switch (callData.parsingError) { |
|
|
|
{ |
|
|
|
case CET_BadParser: |
|
|
|
case CET_BadParser: |
|
|
|
builder.Append(P("Internal error occurred: invalid parser")); |
|
|
|
builder.Append(P("Internal error occurred: invalid parser")); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_NoSubCommands: |
|
|
|
case CET_NoSubCommands: |
|
|
|
builder.Append(P("Ill defined command: no subcommands")); |
|
|
|
builder.Append(P("Ill defined command: no subcommands")); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_BadSubCommand: |
|
|
|
case CET_BadSubCommand: |
|
|
|
builder |
|
|
|
builder.Append(P("Ill defined sub-command: ")) |
|
|
|
.Append(P("Ill defined sub-command: ")) |
|
|
|
.Append(callData.errorCause); |
|
|
|
.Append(callData.errorCause); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_NoRequiredParam: |
|
|
|
case CET_NoRequiredParam: |
|
|
|
builder.Append(P("Missing required parameter: ")) |
|
|
|
builder |
|
|
|
.Append(callData.errorCause); |
|
|
|
.Append(P("Missing required parameter: ")) |
|
|
|
break; |
|
|
|
.Append(callData.errorCause); |
|
|
|
case CET_NoRequiredParamForOption: |
|
|
|
break; |
|
|
|
builder.Append(P("Missing required parameter for option: ")) |
|
|
|
case CET_NoRequiredParamForOption: |
|
|
|
.Append(callData.errorCause); |
|
|
|
builder |
|
|
|
break; |
|
|
|
.Append(P("Missing required parameter for option: ")) |
|
|
|
case CET_UnknownOption: |
|
|
|
.Append(callData.errorCause); |
|
|
|
builder.Append(P("Invalid option specified: ")) |
|
|
|
break; |
|
|
|
.Append(callData.errorCause); |
|
|
|
case CET_UnknownOption: |
|
|
|
break; |
|
|
|
builder |
|
|
|
case CET_UnknownShortOption: |
|
|
|
.Append(P("Invalid option specified: ")) |
|
|
|
builder.Append(P("Invalid short option specified")); |
|
|
|
.Append(callData.errorCause); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_RepeatedOption: |
|
|
|
case CET_UnknownShortOption: |
|
|
|
builder.Append(P("Option specified several times: ")) |
|
|
|
builder.Append(P("Invalid short option specified")); |
|
|
|
.Append(callData.errorCause); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_RepeatedOption: |
|
|
|
case CET_UnusedCommandParameters: |
|
|
|
builder |
|
|
|
builder.Append(P("Part of command could not be parsed: ")) |
|
|
|
.Append(P("Option specified several times: ")) |
|
|
|
.Append(callData.errorCause); |
|
|
|
.Append(callData.errorCause); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_MultipleOptionsWithParams: |
|
|
|
case CET_UnusedCommandParameters: |
|
|
|
builder.Append(P( "Multiple short options in one declarations" |
|
|
|
builder.Append(P("Part of command could not be parsed: ")) |
|
|
|
@ "require parameters: ")) |
|
|
|
.Append(callData.errorCause); |
|
|
|
.Append(callData.errorCause); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_MultipleOptionsWithParams: |
|
|
|
case CET_IncorrectTargetList: |
|
|
|
builder |
|
|
|
builder.Append(P("Target players are incorrectly specified.")) |
|
|
|
.Append(P("Multiple short options in one declarations require parameters: ")) |
|
|
|
.Append(callData.errorCause); |
|
|
|
.Append(callData.errorCause); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case CET_EmptyTargetList: |
|
|
|
case CET_IncorrectTargetList: |
|
|
|
builder.Append(P("List of target players is empty")) |
|
|
|
builder |
|
|
|
.Append(callData.errorCause); |
|
|
|
.Append(P("Target players are incorrectly specified.")) |
|
|
|
break; |
|
|
|
.Append(callData.errorCause); |
|
|
|
default: |
|
|
|
break; |
|
|
|
|
|
|
|
case CET_EmptyTargetList: |
|
|
|
|
|
|
|
builder |
|
|
|
|
|
|
|
.Append(P("List of target players is empty")) |
|
|
|
|
|
|
|
.Append(callData.errorCause); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
} |
|
|
|
} |
|
|
|
result = builder.Copy(); |
|
|
|
result = builder.Copy(); |
|
|
|
builder.FreeSelf(); |
|
|
|
builder.FreeSelf(); |
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Auxiliary method for parsing list of targeted players. |
|
|
|
// Auxiliary method for parsing list of targeted players. |
|
|
|
// Assumes given parser is not `none` and not in a failed state. |
|
|
|
// Assumes given parser is not `none` and not in a failed state. |
|
|
|
// If parsing failed, guaranteed to return an empty array. |
|
|
|
// If parsing failed, guaranteed to return an empty array. |
|
|
|
private final function array<EPlayer> ParseTargets( |
|
|
|
private final function array<EPlayer> ParseTargets(Parser parser, EPlayer callerPlayer) { |
|
|
|
Parser parser, |
|
|
|
|
|
|
|
EPlayer callerPlayer) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
local array<EPlayer> targetPlayers; |
|
|
|
local array<EPlayer> targetPlayers; |
|
|
|
local PlayersParser targetsParser; |
|
|
|
local PlayersParser targetsParser; |
|
|
|
|
|
|
|
|
|
|
@ -646,52 +551,35 @@ private final function array<EPlayer> ParseTargets( |
|
|
|
return targetPlayers; |
|
|
|
return targetPlayers; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Returns name (in lower case) of the caller command class. |
|
|
|
* Returns name (in lower case) of the caller command class. |
|
|
|
public final function Text GetName() { |
|
|
|
* |
|
|
|
|
|
|
|
* @return Name (in lower case) of the caller command class. |
|
|
|
|
|
|
|
* Guaranteed to be not `none`. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final function Text GetName() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (commandData.name == none) { |
|
|
|
if (commandData.name == none) { |
|
|
|
return P("").Copy(); |
|
|
|
return P("").Copy(); |
|
|
|
} |
|
|
|
} |
|
|
|
return commandData.name.LowerCopy(); |
|
|
|
return commandData.name.LowerCopy(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Returns group name (in lower case) of the caller command class. |
|
|
|
* Returns group name (in lower case) of the caller command class. |
|
|
|
public final function Text GetGroupName() { |
|
|
|
* |
|
|
|
|
|
|
|
* @return Group name (in lower case) of the caller command class. |
|
|
|
|
|
|
|
* Guaranteed to be not `none`. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final function Text GetGroupName() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (commandData.group == none) { |
|
|
|
if (commandData.group == none) { |
|
|
|
return P("").Copy(); |
|
|
|
return P("").Copy(); |
|
|
|
} |
|
|
|
} |
|
|
|
return commandData.group.LowerCopy(); |
|
|
|
return commandData.group.LowerCopy(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Returns `Command.Data` struct that describes caller `Command`. |
|
|
|
* Returns `Command.Data` struct that describes caller `Command`. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// Returned struct contains `Text` references that are used internally by the `Command` and |
|
|
|
* @return `Command.Data` that describes caller `Command`. Returned struct |
|
|
|
/// not their copies. |
|
|
|
* contains `Text` references that are used internally by the `Command` |
|
|
|
/// |
|
|
|
* and not their copies. |
|
|
|
/// Generally this is undesired approach and leaves `Command` more vulnerable to modification, |
|
|
|
* Generally this is undesired approach and leaves `Command` more |
|
|
|
/// but copying all the data inside would not only introduce a largely pointless computational |
|
|
|
* vulnerable to modification, but copying all the data inside would not |
|
|
|
/// overhead, but also would require some cumbersome logic. |
|
|
|
* only introduce a largely pointless computational overhead, but also |
|
|
|
/// This might change in the future, so deallocating any objects in the returned `struct` would lead |
|
|
|
* would require some cumbersome logic. This might change in the future, |
|
|
|
/// to undefined behavior. |
|
|
|
* so deallocating any objects in the returned `struct` would lead to |
|
|
|
public final function Data BorrowData() { |
|
|
|
* undefined behavior. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final function Data BorrowData() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return commandData; |
|
|
|
return commandData; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
defaultproperties |
|
|
|
defaultproperties { |
|
|
|
{ |
|
|
|
|
|
|
|
} |
|
|
|
} |