Browse Source

Add command groups support

pull/8/head
Anton Tarasenko 2 years ago
parent
commit
3f45423dc8
  1. 216
      sources/Commands/BuiltInCommands/ACommandHelp.uc
  2. 16
      sources/Commands/Command.uc
  3. 30
      sources/Commands/CommandDataBuilder.uc
  4. 118
      sources/Commands/Commands_Feature.uc

216
sources/Commands/BuiltInCommands/ACommandHelp.uc

@ -28,27 +28,35 @@ var public const int TKEY, TDOUBLE_KEY, TCOMMA_SPACE, TBOOLEAN, TINDENT;
var public const int TBOOLEAN_TRUE_FALSE, TBOOLEAN_ENABLE_DISABLE; var public const int TBOOLEAN_TRUE_FALSE, TBOOLEAN_ENABLE_DISABLE;
var public const int TBOOLEAN_ON_OFF, TBOOLEAN_YES_NO; var public const int TBOOLEAN_ON_OFF, TBOOLEAN_YES_NO;
var public const int TOPTIONS, TCMD_WITH_TARGET, TCMD_WITHOUT_TARGET; var public const int TOPTIONS, TCMD_WITH_TARGET, TCMD_WITHOUT_TARGET;
var public const int TSEPARATOR, TLIST_REGIRESTED_CMDS, TEMPTY_GROUP;
protected function BuildData(CommandDataBuilder builder) protected function BuildData(CommandDataBuilder builder)
{ {
builder.Name(P("help")) builder.Name(P("help")).Group(P("core"))
.Summary(P("Detailed information about available commands.")); .Summary(P("Displays detailed information about available commands."));
builder.OptionalParams() builder.OptionalParams()
.ParamTextList(P("commands")) .ParamTextList(P("commands"))
.Describe(P("Display information about all specified commands.")); .Describe(P("Displays information about all specified commands."));
builder.Option(P("list")) builder.Option(P("list"))
.Describe(P("Display list of all available commands.")); .Describe(P("Display available commands. Optionally command groups can"
@ "be specified and then only commands from such groups will be"
@ "listed. Otherwise all commands will be displayed."))
.OptionalParams()
.ParamTextList(P("groups"));
} }
protected function Executed(Command.CallData callData, EPlayer callerPlayer) protected function Executed(Command.CallData callData, EPlayer callerPlayer)
{ {
local HashTable parameters, options;; local HashTable parameters, options;;
local ArrayList commandsToDisplay; local ArrayList commandsToDisplay, commandGroupsToDisplay;
parameters = callData.parameters; parameters = callData.parameters;
options = callData.options; options = callData.options;
// Print command list if "--list" option was specified // Print command list if "--list" option was specified
if (options.HasKey(P("list"))) { if (options.HasKey(P("list")))
DisplayCommandList(callerPlayer); {
commandGroupsToDisplay = options.GetArrayListBy(P("/list/groups"));
DisplayCommandLists(commandGroupsToDisplay);
_.memory.Free(commandGroupsToDisplay);
} }
// Help pages. // Help pages.
// Only need to print them if: // Only need to print them if:
@ -58,58 +66,95 @@ protected function Executed(Command.CallData callData, EPlayer callerPlayer)
if (!options.HasKey(P("list")) || parameters.HasKey(P("commands"))) if (!options.HasKey(P("list")) || parameters.HasKey(P("commands")))
{ {
commandsToDisplay = parameters.GetArrayList(P("commands")); commandsToDisplay = parameters.GetArrayList(P("commands"));
DisplayCommandHelpPages(callerPlayer, commandsToDisplay); DisplayCommandHelpPages(commandsToDisplay);
_.memory.Free(commandsToDisplay); _.memory.Free(commandsToDisplay);
} }
} }
private final function DisplayCommandList(EPlayer player) private final function DisplayCommandLists(ArrayList commandGroupsToDisplay)
{ {
local int i; local int i;
local ConsoleWriter console; local array<Text> commandNames, groupsNames;
local Command nextCommand;
local Command.Data nextData;
local array<Text> commandNames;
local Commands_Feature commandsFeature; local Commands_Feature commandsFeature;
if (player == none) return;
commandsFeature = commandsFeature =
Commands_Feature(class'Commands_Feature'.static.GetEnabledInstance()); Commands_Feature(class'Commands_Feature'.static.GetEnabledInstance());
if (commandsFeature == none) return; if (commandsFeature == none) {
return;
console = player.BorrowConsole(); }
commandNames = commandsFeature.GetCommandNames(); if (commandGroupsToDisplay == none)
for (i = 0; i < commandNames.length; i += 1)
{ {
nextCommand = commandsFeature.GetCommand(commandNames[i]); groupsNames = commandsFeature.GetGroupsNames();
if (nextCommand == none) continue; DisplayCommandsNamesArray(commandsFeature, commandNames);
}
else
{
for (i = 0; i < commandGroupsToDisplay.GetLength(); i += 1) {
groupsNames[groupsNames.length] = commandGroupsToDisplay.GetText(i);
}
}
callerConsole.WriteLine(T(TLIST_REGIRESTED_CMDS));
for (i = 0; i < groupsNames.length; i += 1)
{
if (groupsNames[i] == none) {
continue;
}
commandNames = commandsFeature.GetCommandNamesInGroup(groupsNames[i]);
if (commandNames.length > 0)
{
callerConsole.UseColorOnce(_.color.TextSubHeader);
if (groupsNames[i].IsEmpty()) {
callerConsole.WriteLine(T(TEMPTY_GROUP));
}
else {
callerConsole.WriteLine(groupsNames[i]);
}
DisplayCommandsNamesArray(commandsFeature, commandNames);
_.memory.FreeMany(commandNames);
}
}
_.memory.FreeMany(groupsNames);
}
private final function DisplayCommandsNamesArray(
Commands_Feature commandsFeature,
array<Text> commandsNamesArray)
{
local int i;
local Command nextCommand;
local Command.Data nextData;
for (i = 0; i < commandsNamesArray.length; i += 1)
{
nextCommand = commandsFeature.GetCommand(commandsNamesArray[i]);
if (nextCommand == none) {
continue;
}
nextData = nextCommand.BorrowData(); nextData = nextCommand.BorrowData();
console.UseColor(_.color.textEmphasis) callerConsole.UseColor(_.color.textEmphasis)
.Write(nextData.name) .Write(nextData.name)
.ResetColor() .ResetColor()
.Write(T(TCOLUMN_SPACE)) .Write(T(TCOLUMN_SPACE))
.WriteLine(nextData.summary); .WriteLine(nextData.summary);
_.memory.Free(nextCommand);
} }
_.memory.FreeMany(commandNames);
} }
private final function DisplayCommandHelpPages( private final function DisplayCommandHelpPages(ArrayList commandList)
EPlayer player,
ArrayList commandList)
{ {
local int i; local int i;
local Text nextCommandName; local Text nextCommandName;
local Command nextCommand; local Command nextCommand;
local Commands_Feature commandsFeature; local Commands_Feature commandsFeature;
if (player == none) return;
commandsFeature = commandsFeature =
Commands_Feature(class'Commands_Feature'.static.GetEnabledInstance()); Commands_Feature(class'Commands_Feature'.static.GetEnabledInstance());
if (commandsFeature == none) return; if (commandsFeature == none) {
return;
}
// If arguments were empty - at least display our own help page // If arguments were empty - at least display our own help page
if (commandList == none) if (commandList == none)
{ {
PrintHelpPage(player.BorrowConsole(), BorrowData()); PrintHelpPage(BorrowData());
return; return;
} }
// Otherwise - print help for specified commands // Otherwise - print help for specified commands
@ -118,70 +163,74 @@ private final function DisplayCommandHelpPages(
nextCommandName = commandList.GetText(i); nextCommandName = commandList.GetText(i);
nextCommand = commandsFeature.GetCommand(nextCommandName); nextCommand = commandsFeature.GetCommand(nextCommandName);
_.memory.Free(nextCommandName); _.memory.Free(nextCommandName);
if (nextCommand == none) continue; if (nextCommand == none) {
PrintHelpPage(player.BorrowConsole(), nextCommand.BorrowData()); continue;
}
if (i > 0) {
callerConsole.WriteLine(T(TSEPARATOR));
}
PrintHelpPage(nextCommand.BorrowData());
_.memory.Free(nextCommand);
} }
} }
// Following methods are mostly self-explanatory, // Following methods are mostly self-explanatory
// all assume that passed `cout != none` private final function PrintHelpPage(Command.Data data)
private final function PrintHelpPage(ConsoleWriter cout, Command.Data data)
{ {
local Text commandNameUpperCase; local Text commandNameUpperCase;
// Get capitalized command name // Get capitalized command name
commandNameUpperCase = data.name.UpperCopy(); commandNameUpperCase = data.name.UpperCopy();
// Print header: name + basic info // Print header: name + basic info
cout.UseColor(_.color.textHeader) callerConsole.UseColor(_.color.textHeader)
.Write(commandNameUpperCase) .Write(commandNameUpperCase)
.UseColor(_.color.textDefault); .UseColor(_.color.textDefault);
commandNameUpperCase.FreeSelf(); commandNameUpperCase.FreeSelf();
if (data.requiresTarget) { if (data.requiresTarget) {
cout.WriteLine(T(TCMD_WITH_TARGET)); callerConsole.WriteLine(T(TCMD_WITH_TARGET));
} }
else { else {
cout.WriteLine(T(TCMD_WITHOUT_TARGET)); callerConsole.WriteLine(T(TCMD_WITHOUT_TARGET));
} }
// Print commands and options // Print commands and options
PrintCommands(cout, data); PrintCommands(data);
PrintOptions(cout, data); PrintOptions(data);
// Clean up // Clean up
cout.ResetColor().Flush(); callerConsole.ResetColor().Flush();
} }
private final function PrintCommands(ConsoleWriter cout, Command.Data data) private final function PrintCommands(Command.Data data)
{ {
local int i; local int i;
local array<SubCommand> subCommands; local array<SubCommand> subCommands;
subCommands = data.subCommands; subCommands = data.subCommands;
for (i = 0; i < subCommands.length; i += 1) { for (i = 0; i < subCommands.length; i += 1) {
PrintSubCommand(cout, subCommands[i], data.name); PrintSubCommand(subCommands[i], data.name);
} }
} }
private final function PrintSubCommand( private final function PrintSubCommand(
ConsoleWriter cout, SubCommand subCommand,
SubCommand subCommand, BaseText commandName)
BaseText commandName)
{ {
// Command + parameters // Command + parameters
// Command name + sub command name // Command name + sub command name
cout.UseColor(_.color.textEmphasis) callerConsole.UseColor(_.color.textEmphasis)
.Write(commandName) .Write(commandName)
.Write(T(TSPACE)); .Write(T(TSPACE));
if (subCommand.name != none && !subCommand.name.IsEmpty()) { if (subCommand.name != none && !subCommand.name.IsEmpty()) {
cout.Write(subCommand.name).Write(T(TSPACE)); callerConsole.Write(subCommand.name).Write(T(TSPACE));
} }
cout.UseColor(_.color.textDefault); callerConsole.UseColor(_.color.textDefault);
// Parameters // Parameters
PrintParameters(cout, subCommand.required, subCommand.optional); PrintParameters(subCommand.required, subCommand.optional);
cout.Flush(); callerConsole.Flush();
// Description // Description
if (subCommand.description != none && !subCommand.description.IsEmpty()) { if (subCommand.description != none && !subCommand.description.IsEmpty()) {
cout.WriteBlock(subCommand.description); callerConsole.WriteBlock(subCommand.description);
} }
} }
private final function PrintOptions(ConsoleWriter cout, Command.Data data) private final function PrintOptions(Command.Data data)
{ {
local int i; local int i;
local array<Option> options; local array<Option> options;
@ -189,22 +238,22 @@ private final function PrintOptions(ConsoleWriter cout, Command.Data data)
if (options.length <= 0) { if (options.length <= 0) {
return; return;
} }
cout.UseColor(_.color.textSubHeader) callerConsole
.UseColor(_.color.textSubHeader)
.WriteLine(T(TOPTIONS)) .WriteLine(T(TOPTIONS))
.UseColor(_.color.textDefault); .UseColor(_.color.textDefault);
for (i = 0; i < options.length; i += 1) { for (i = 0; i < options.length; i += 1) {
PrintOption(cout, options[i]); PrintOption(options[i]);
} }
} }
private final function PrintOption( private final function PrintOption(Option option)
ConsoleWriter cout,
Option option)
{ {
local Text shortNameAsText; local Text shortNameAsText;
// Option short and long names with added key characters // Option short and long names with added key characters
shortNameAsText = _.text.FromCharacter(option.shortName); shortNameAsText = _.text.FromCharacter(option.shortName);
cout.UseColor(_.color.textEmphasis) callerConsole
.UseColor(_.color.textEmphasis)
.Write(T(TKEY)).Write(shortNameAsText) // "-" .Write(T(TKEY)).Write(shortNameAsText) // "-"
.UseColor(_.color.textDefault) .UseColor(_.color.textDefault)
.Write(T(TCOMMA_SPACE)) // ", " .Write(T(TCOMMA_SPACE)) // ", "
@ -215,18 +264,17 @@ private final function PrintOption(
// Parameters // Parameters
if (option.required.length != 0 || option.optional.length != 0) if (option.required.length != 0 || option.optional.length != 0)
{ {
cout.Write(T(TSPACE)); callerConsole.Write(T(TSPACE));
PrintParameters(cout, option.required, option.optional); PrintParameters(option.required, option.optional);
} }
cout.Flush(); callerConsole.Flush();
// Description // Description
if (option.description != none && !option.description.IsEmpty()) { if (option.description != none && !option.description.IsEmpty()) {
cout.WriteBlock(option.description); callerConsole.WriteBlock(option.description);
} }
} }
private final function PrintParameters( private final function PrintParameters(
ConsoleWriter cout,
array<Parameter> required, array<Parameter> required,
array<Parameter> optional) array<Parameter> optional)
{ {
@ -234,57 +282,57 @@ private final function PrintParameters(
// Print required // Print required
for (i = 0; i < required.length; i += 1) for (i = 0; i < required.length; i += 1)
{ {
PrintParameter(cout, required[i]); PrintParameter(required[i]);
if (i < required.length - 1) { if (i < required.length - 1) {
cout.Write(T(TSPACE)); callerConsole.Write(T(TSPACE));
} }
} }
if (optional.length <= 0) { if (optional.length <= 0) {
return; return;
} }
// Print optional // Print optional
cout.Write(T(TSPACE)).Write(T(TOPEN_BRACKET)); callerConsole.Write(T(TSPACE)).Write(T(TOPEN_BRACKET));
for (i = 0; i < optional.length; i += 1) for (i = 0; i < optional.length; i += 1)
{ {
PrintParameter(cout, optional[i]); PrintParameter(optional[i]);
if (i < optional.length - 1) { if (i < optional.length - 1) {
cout.Write(T(TSPACE)); callerConsole.Write(T(TSPACE));
} }
} }
cout.Write(T(TCLOSE_BRACKET)); callerConsole.Write(T(TCLOSE_BRACKET));
} }
private final function PrintParameter(ConsoleWriter cout, Parameter parameter) private final function PrintParameter(Parameter parameter)
{ {
switch (parameter.type) switch (parameter.type)
{ {
case CPT_Boolean: case CPT_Boolean:
cout.UseColor(_.color.typeBoolean); callerConsole.UseColor(_.color.typeBoolean);
break; break;
case CPT_Integer: case CPT_Integer:
cout.UseColor(_.color.typeNumber); callerConsole.UseColor(_.color.typeNumber);
break; break;
case CPT_Number: case CPT_Number:
cout.UseColor(_.color.typeNumber); callerConsole.UseColor(_.color.typeNumber);
break; break;
case CPT_Text: case CPT_Text:
case CPT_Remainder: case CPT_Remainder:
cout.UseColor(_.color.typeString); callerConsole.UseColor(_.color.typeString);
break; break;
case CPT_Object: case CPT_Object:
cout.UseColor(_.color.typeLiteral); callerConsole.UseColor(_.color.typeLiteral);
break; break;
case CPT_Array: case CPT_Array:
cout.UseColor(_.color.typeLiteral); callerConsole.UseColor(_.color.typeLiteral);
break; break;
default: default:
cout.UseColor(_.color.textDefault); callerConsole.UseColor(_.color.textDefault);
} }
cout.Write(parameter.displayName); callerConsole.Write(parameter.displayName);
if (parameter.allowsList) { if (parameter.allowsList) {
cout.Write(T(TPLUS)); callerConsole.Write(T(TPLUS));
} }
cout.UseColor(_.color.textDefault); callerConsole.UseColor(_.color.textDefault);
} }
defaultproperties defaultproperties
@ -323,4 +371,10 @@ defaultproperties
stringConstants(15) = ": This command does not require target to be specified." stringConstants(15) = ": This command does not require target to be specified."
TOPTIONS = 16 TOPTIONS = 16
stringConstants(16) = "OPTIONS" stringConstants(16) = "OPTIONS"
TSEPARATOR = 17
stringConstants(17) = "============================="
TLIST_REGIRESTED_CMDS = 18
stringConstants(18) = "{$TextHeader List of registered commands}"
TEMPTY_GROUP = 19
stringConstants(19) = "Empty group"
} }

16
sources/Commands/Command.uc

@ -164,6 +164,8 @@ 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
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;
@ -563,6 +565,20 @@ public final function Text GetName()
return commandData.name.LowerCopy(); return commandData.name.LowerCopy();
} }
/**
* Returns group name (in lower case) of the caller command class.
*
* @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) {
return P("").Copy();
}
return commandData.group.LowerCopy();
}
// TODO: use `SharedRef` instead // TODO: use `SharedRef` instead
/** /**
* Returns `Command.Data` struct that describes caller `Command`. * Returns `Command.Data` struct that describes caller `Command`.

30
sources/Commands/CommandDataBuilder.uc

@ -1,7 +1,7 @@
/** /**
* Utility class that provides developers with a simple interface to * Utility class that provides developers with a simple interface to
* prepare data that describes command's parameters and options. * prepare data that describes command's parameters and options.
* Copyright 2021 Anton Tarasenko * Copyright 2021-2022 Anton Tarasenko
*------------------------------------------------------------------------------ *------------------------------------------------------------------------------
* This file is part of Acedia. * This file is part of Acedia.
* *
@ -42,7 +42,8 @@ class CommandDataBuilder extends AcediaObject
*/ */
// "Prepared data" // "Prepared data"
var private Text commandName, commandSummary; var private Text commandName, commandGroup;
var private Text commandSummary;
var private array<Command.SubCommand> subcommands; var private array<Command.SubCommand> subcommands;
var private array<Command.Option> options; var private array<Command.Option> options;
var private bool requiresTarget; var private bool requiresTarget;
@ -84,6 +85,7 @@ protected function Finalizer()
optionsIsOptional.length = 0; optionsIsOptional.length = 0;
selectedParameterArray.length = 0; selectedParameterArray.length = 0;
commandName = none; commandName = none;
commandGroup = none;
commandSummary = none; commandSummary = none;
selectedItemName = none; selectedItemName = none;
selectedDescription = none; selectedDescription = none;
@ -474,6 +476,29 @@ public final function CommandDataBuilder Name(BaseText newName)
return self; return self;
} }
/**
* Sets new group of `Command.Data` under construction. Group name is meant to
* be shared among several commands, allowing user to filter or fetch commands
* of a certain group.
*
* @return Returns the caller `CommandDataBuilder` to allow for
* method chaining.
*/
public final function CommandDataBuilder Group(BaseText newName)
{
if (newName != none && newName == commandGroup) {
return self;
}
_.memory.Free(commandGroup);
if (newName != none) {
commandGroup = newName.Copy();
}
else {
commandGroup = none;
}
return self;
}
/** /**
* Sets new summary of `Command.Data` under construction. Summary gives a short * Sets new summary of `Command.Data` under construction. Summary gives a short
* description of the command on the whole, to be displayed in a command list. * description of the command on the whole, to be displayed in a command list.
@ -545,6 +570,7 @@ public final function Command.Data BorrowData()
local Command.Data newData; local Command.Data newData;
RecordSelection(); RecordSelection();
newData.name = commandName; newData.name = commandName;
newData.group = commandGroup;
newData.summary = commandSummary; newData.summary = commandSummary;
newData.subcommands = subcommands; newData.subcommands = subcommands;
newData.options = options; newData.options = options;

118
sources/Commands/Commands_Feature.uc

@ -27,6 +27,9 @@ var private array<Text> commandDelimiters;
// Registered commands, recorded as (<command_name>, <command_instance>) pairs. // Registered commands, recorded as (<command_name>, <command_instance>) pairs.
// Keys should be deallocated when their entry is removed. // Keys should be deallocated when their entry is removed.
var private HashTable registeredCommands; var private HashTable registeredCommands;
// `HashTable` of "<command_group_name>" <-> `ArrayList` of commands pairs
// to allow quick fetch of commands belonging to a single group
var private HashTable groupedCommands;
// When this flag is set to true, mutate input becomes available // When this flag is set to true, mutate input becomes available
// despite `useMutateInput` flag to allow to unlock server in case of an error // despite `useMutateInput` flag to allow to unlock server in case of an error
@ -48,16 +51,17 @@ var LoggerAPI.Definition errCommandDuplicate;
protected function OnEnabled() protected function OnEnabled()
{ {
registeredCommands = _.collections.EmptyHashTable(); registeredCommands = _.collections.EmptyHashTable();
groupedCommands = _.collections.EmptyHashTable();
RegisterCommand(class'ACommandHelp'); RegisterCommand(class'ACommandHelp');
// Macro selector // Macro selector
commandDelimiters[0] = P("@"); commandDelimiters[0] = _.text.FromString("@");
// Key selector // Key selector
commandDelimiters[1] = P("#"); commandDelimiters[1] = _.text.FromString("#");
// Player array (possibly JSON array) // Player array (possibly JSON array)
commandDelimiters[2] = P("["); commandDelimiters[2] = _.text.FromString("[");
// Negation of the selector // Negation of the selector
commandDelimiters[3] = P("!"); commandDelimiters[3] = _.text.FromString("!");
} }
protected function OnDisabled() protected function OnDisabled()
@ -71,10 +75,13 @@ protected function OnDisabled()
useChatInput = false; useChatInput = false;
useMutateInput = false; useMutateInput = false;
_.memory.Free(registeredCommands); _.memory.Free(registeredCommands);
registeredCommands = none; _.memory.Free(groupedCommands);
commandDelimiters.length = 0;
_.memory.Free(chatCommandPrefix); _.memory.Free(chatCommandPrefix);
chatCommandPrefix = none; _.memory.FreeMany(commandDelimiters);
registeredCommands = none;
groupedCommands = none;
chatCommandPrefix = none;
commandDelimiters.length = 0;
} }
protected function SwapConfig(FeatureConfig config) protected function SwapConfig(FeatureConfig config)
@ -207,7 +214,8 @@ public final static function Text GetChatPrefix()
*/ */
public final function RegisterCommand(class<Command> commandClass) public final function RegisterCommand(class<Command> commandClass)
{ {
local Text commandName; local Text commandName, groupName;
local ArrayList groupArray;
local Command newCommandInstance, existingCommandInstance; local Command newCommandInstance, existingCommandInstance;
if (commandClass == none) return; if (commandClass == none) return;
@ -215,6 +223,7 @@ public final function RegisterCommand(class<Command> commandClass)
newCommandInstance = Command(_.memory.Allocate(commandClass, true)); newCommandInstance = Command(_.memory.Allocate(commandClass, true));
commandName = newCommandInstance.GetName(); commandName = newCommandInstance.GetName();
groupName = newCommandInstance.GetGroupName();
// Check for duplicates and report them // Check for duplicates and report them
existingCommandInstance = Command(registeredCommands.GetItem(commandName)); existingCommandInstance = Command(registeredCommands.GetItem(commandName));
if (existingCommandInstance != none) if (existingCommandInstance != none)
@ -223,6 +232,7 @@ public final function RegisterCommand(class<Command> commandClass)
.ArgClass(existingCommandInstance.class) .ArgClass(existingCommandInstance.class)
.Arg(commandName) .Arg(commandName)
.ArgClass(commandClass); .ArgClass(commandClass);
_.memory.Free(groupName);
_.memory.Free(newCommandInstance); _.memory.Free(newCommandInstance);
_.memory.Free(existingCommandInstance); _.memory.Free(existingCommandInstance);
return; return;
@ -230,6 +240,16 @@ public final function RegisterCommand(class<Command> commandClass)
// Otherwise record new command // Otherwise record new command
// `commandName` used as a key, do not deallocate it // `commandName` used as a key, do not deallocate it
registeredCommands.SetItem(commandName, newCommandInstance); registeredCommands.SetItem(commandName, newCommandInstance);
// Add to grouped collection
groupArray = groupedCommands.GetArrayList(groupName);
if (groupArray == none) {
groupArray = _.collections.EmptyArrayList();
}
groupArray.AddItem(newCommandInstance);
groupedCommands.SetItem(groupName, groupArray);
_.memory.Free(groupArray);
_.memory.Free(groupName);
_.memory.Free(commandName);
_.memory.Free(newCommandInstance); _.memory.Free(newCommandInstance);
} }
@ -249,6 +269,7 @@ public final function RemoveCommand(class<Command> commandClass)
local Iter iter; local Iter iter;
local Command nextCommand; local Command nextCommand;
local Text nextCommandName; local Text nextCommandName;
local array<Text> commandGroup;
local array<Text> keysToRemove; local array<Text> keysToRemove;
if (commandClass == none) return; if (commandClass == none) return;
@ -266,6 +287,7 @@ public final function RemoveCommand(class<Command> commandClass)
continue; continue;
} }
keysToRemove[keysToRemove.length] = nextCommandName; keysToRemove[keysToRemove.length] = nextCommandName;
commandGroup[commandGroup.length] = nextCommand.GetGroupName();
_.memory.Free(nextCommand); _.memory.Free(nextCommand);
} }
iter.FreeSelf(); iter.FreeSelf();
@ -274,6 +296,40 @@ public final function RemoveCommand(class<Command> commandClass)
registeredCommands.RemoveItem(keysToRemove[i]); registeredCommands.RemoveItem(keysToRemove[i]);
_.memory.Free(keysToRemove[i]); _.memory.Free(keysToRemove[i]);
} }
for (i = 0; i < commandGroup.length; i += 1) {
RemoveClassFromGroup(commandClass, commandGroup[i]);
}
_.memory.FreeMany(commandGroup);
}
private final function RemoveClassFromGroup(
class<Command> commandClass,
BaseText commandGroup)
{
local int i;
local ArrayList groupArray;
local Command nextCommand;
groupArray = groupedCommands.GetArrayList(commandGroup);
if (groupArray == none) {
return;
}
while (i < groupArray.GetLength())
{
nextCommand = Command(groupArray.GetItem(i));
if (nextCommand != none && nextCommand.class == commandClass) {
groupArray.RemoveIndex(i);
}
else {
i += 1;
}
_.memory.Free(nextCommand);
}
if (groupArray.GetLength() == 0) {
groupedCommands.RemoveItem(commandGroup);
}
_.memory.Free(groupArray);
} }
/** /**
@ -313,6 +369,50 @@ public final function array<Text> GetCommandNames()
return emptyResult; return emptyResult;
} }
/**
* Returns array of names of all available commands belonging to the group
* `groupName`.
*
* @return Array of names of all available (registered) commands, belonging to
* the group `groupName`.
*/
public final function array<Text> GetCommandNamesInGroup(BaseText groupName)
{
local int i;
local ArrayList groupArray;
local Command nextCommand;
local array<Text> result;
if (groupedCommands == none) return result;
groupArray = groupedCommands.GetArrayList(groupName);
if (groupArray == none) return result;
for (i = 0; i < groupArray.GetLength(); i += 1)
{
nextCommand = Command(groupArray.GetItem(i));
if (nextCommand != none) {
result[result.length] = nextCommand.GetName();
}
_.memory.Free(nextCommand);
}
return result;
}
/**
* Returns all available command groups' names.
*
* @return Array of all available command groups' names.
*/
public final function array<Text> GetGroupsNames()
{
local array<Text> emptyResult;
if (groupedCommands != none) {
return groupedCommands.GetTextKeys();
}
return emptyResult;
}
/** /**
* Handles user input: finds appropriate command and passes the rest of * Handles user input: finds appropriate command and passes the rest of
* the arguments to it for further processing. * the arguments to it for further processing.

Loading…
Cancel
Save