Add "sideeffects" command for viewing active side effects #13

Merged
dkanus merged 3 commits from feature_side_effects_command into develop 2 years ago
  1. 193
      sources/BaseAPI/API/Commands/BuiltInCommands/ACommandSideEffects.uc
  2. 1
      sources/BaseAPI/API/Commands/Commands_Feature.uc
  3. 18
      sources/BaseAPI/API/SideEffects/SideEffectAPI.uc
  4. 16
      sources/Users/User.uc

193
sources/BaseAPI/API/Commands/BuiltInCommands/ACommandSideEffects.uc

@ -0,0 +1,193 @@
/**
* Author: dkanus
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore
* License: GPL
* Copyright 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 <https://www.gnu.org/licenses/>.
*/
class ACommandSideEffects extends Command;
// Maps `UserID` to `ArrayList` with side effects listed for that player last time
var private HashTable displayedLists;
protected function Constructor() {
super.Constructor();
displayedLists = _.collections.EmptyHashTable();
}
protected function Finalizer() {
super.Finalizer();
_.memory.Free(displayedLists);
displayedLists = none;
}
protected function BuildData(CommandDataBuilder builder) {
builder.Name(P("sideeffects"));
builder.Group(P("core"));
builder.Summary(P("Displays information about current side effects."));
builder.Describe(P("This command allows to display current side effects, optionally filtering"
@ "them by specified package names."));
builder.OptionalParams();
builder.ParamTextList(P("package_names"));
builder.SubCommand(P("show"));
builder.Describe(P("This sub-command is only usable after side effects have been shown"
@ "at least once. It takes an index from the last displayed list and displays a verbose"
@ "information about it."));
builder.ParamInteger(P("side_effect_number"));
builder.Option(P("verbose"));
builder.Describe(P("Display verbose information about each side effect."));
}
protected function Executed(CallData arguments, EPlayer instigator) {
local UserID playerID;
local array<SideEffect> relevantSideEffects;
local ArrayList packagesList, storedSideEffectsList;
playerID = instigator.GetUserID();
if (arguments.subCommandName.IsEmpty()) {
relevantSideEffects = _.sideEffects.GetAll();
packagesList = arguments.parameters.GetArrayList(P("package_names"));
FilterSideEffects(/*out*/ relevantSideEffects, packagesList);
_.memory.Free(packagesList);
DisplaySideEffects(relevantSideEffects, arguments.options.HasKey(P("verbose")));
// Store new side effect list
storedSideEffectsList = _.collections.NewArrayList(relevantSideEffects);
displayedLists.SetItem(playerID, storedSideEffectsList);
_.memory.FreeMany(relevantSideEffects);
_.memory.Free(storedSideEffectsList);
} else {
ShowInfoFor(playerID, arguments.parameters.GetInt(P("side_effect_number")));
}
_.memory.Free(playerID);
}
private function FilterSideEffects(out array<SideEffect> sideEffects, ArrayList allowedPackages) {
local int i, j;
local int packagesLength;
local bool matchedPackage;
local Text nextSideEffectPackage, nextAllowedPackage;
if (allowedPackages == none) return;
if (allowedPackages.GetLength() <= 0) return;
packagesLength = allowedPackages.GetLength();
while (i < sideEffects.length) {
nextSideEffectPackage = sideEffects[i].GetPackage();
matchedPackage = false;
for (j = 0; j < packagesLength; j += 1) {
nextAllowedPackage = allowedPackages.GetText(j);
if (nextAllowedPackage.Compare(nextSideEffectPackage, SCASE_INSENSITIVE)) {
matchedPackage = true;
_.memory.Free(nextAllowedPackage);
break;
}
_.memory.Free(nextAllowedPackage);
}
if (!matchedPackage) {
sideEffects.Remove(i, 1);
} else {
i += 1;
}
_.memory.Free(nextSideEffectPackage);
}
}
private function DisplaySideEffects(array<SideEffect> toDisplay, bool verbose) {
local int i;
local MutableText nextPrefix;
if (toDisplay.length <= 0) {
callerConsole.Write(F("List of side effects is {$TextNeutral empty}."));
}
for (i = 0; i < toDisplay.length; i += 1) {
nextPrefix = _.text.FromIntM(i + 1);
nextPrefix.Append(P("."));
DisplaySideEffect(toDisplay[i], nextPrefix, verbose);
_.memory.Free(nextPrefix);
}
}
private function DisplaySideEffect(SideEffect toDisplay, BaseText prefix, bool verbose) {
local Text effectName, effectDescription, effectPackage, effectSource, effectStatus;
if (toDisplay == none) {
return;
}
if (prefix != none) {
callerConsole.Write(prefix);
callerConsole.Write(P(" "));
}
effectName = toDisplay.GetName();
effectPackage = toDisplay.GetPackage();
effectSource = toDisplay.GetSource();
effectStatus = toDisplay.GetStatus();
callerConsole.UseColor(_.color.TextEmphasis);
callerConsole.Write(P("["));
callerConsole.Write(effectPackage);
callerConsole.Write(P(" \\ "));
callerConsole.Write(effectSource);
callerConsole.Write(P("] "));
callerConsole.ResetColor();
callerConsole.Write(effectName);
callerConsole.Write(P(" {"));
callerConsole.Write(effectStatus);
callerConsole.WriteLine(P("}"));
if (verbose) {
effectDescription = toDisplay.GetDescription();
callerConsole.WriteBlock(effectDescription);
}
_.memory.Free5(effectName, effectDescription, effectPackage, effectSource, effectStatus);
}
private function ShowInfoFor(UserID playerID, int sideEffectIndex) {
local SideEffect toDisplay;
local ArrayList sideEffectList;
if (playerID == none) {
return;
}
if (sideEffectIndex <= 0) {
callerConsole.WriteLine(F("Specified side effect index {$TextNegative isn't positive}!"));
return;
}
sideEffectList = displayedLists.GetArrayList(playerID);
if (sideEffectList == none) {
callerConsole.WriteLine(F("{$TextNegative Cannot display} side effect by index without"
@ "first listing them. Call {$TextEmphasis sideeffects} command without"
@ "{$TextEmphasis show} subcommand first."));
return;
}
if (sideEffectIndex > sideEffectList.GetLength()) {
callerConsole.WriteLine(F("Specified side effect index is {$TextNegative out of bounds}."));
_.memory.Free(sideEffectList);
return;
}
// Above we checked that `sideEffectIndex` lies within `[0; sideEffectList.GetLength()]` segment
// This means that `sideEffectIndex - 1` points at non-`none` value
toDisplay = SideEffect(sideEffectList.GetItem(sideEffectIndex - 1));
if (!_.sideEffects.IsRegistered(toDisplay)) {
callerConsole.UseColorOnce(_.color.TextWarning);
callerConsole.WriteLine(P("Selected side effect is no longer active!"));
}
DisplaySideEffect(toDisplay, none, true);
_.memory.Free2(toDisplay, sideEffectList);
}
defaultproperties {
}

1
sources/BaseAPI/API/Commands/Commands_Feature.uc

@ -110,6 +110,7 @@ protected function OnEnabled() {
RegisterCommand(class'ACommandHelp'); RegisterCommand(class'ACommandHelp');
RegisterCommand(class'ACommandNotify'); RegisterCommand(class'ACommandNotify');
RegisterCommand(class'ACommandVote'); RegisterCommand(class'ACommandVote');
RegisterCommand(class'ACommandSideEffects');
if (_.environment.IsDebugging()) { if (_.environment.IsDebugging()) {
RegisterCommand(class'ACommandFakers'); RegisterCommand(class'ACommandFakers');
} }

18
sources/BaseAPI/API/SideEffects/SideEffectAPI.uc

@ -120,6 +120,24 @@ public function SideEffect Add_S(
return newSideEffect; return newSideEffect;
} }
/// Checks whether specified [`SideEffect`] is currently active.
///
/// Check is done via contents and not instance equality.
/// Returns `true` if specified [`SideEffect`] is currently active and `false` otherwise.
public function bool IsRegistered(SideEffect sideEffectToCheck) {
local int i;
if (sideEffectToCheck == none) return false;
if (!sideEffectToCheck.IsInitialized()) return false;
for (i = 0; i < activeSideEffects.length; i += 1) {
if (activeSideEffects[i].IsEqual(sideEffectToCheck)) {
return true;
}
}
return false;
}
/// Adds a new side effect to the list of active side effects. /// Adds a new side effect to the list of active side effects.
/// ///
/// This method will fail if its argument is `none`, non-initialized or a side effect with that /// This method will fail if its argument is `none`, non-initialized or a side effect with that

16
sources/Users/User.uc

@ -176,6 +176,22 @@ public final function bool SetPersistentData(
return result; return result;
} }
public function bool IsEqual(Object other) {
local User otherUser;
if (id == none) return false;
otherUser = User(other);
if (otherUser == none) return false;
if (otherUser.id == none) return false;
return id.IsEqual(otherUser.id);
}
protected function int CalculateHashCode() {
// If `id` is `none`, then caller `User` shouldn't be used at all
return id.GetHashCode();
}
defaultproperties defaultproperties
{ {
} }
Loading…
Cancel
Save