diff --git a/config/AcediaAliases.ini b/config/AcediaAliases.ini
index 891308c..8f77a09 100644
--- a/config/AcediaAliases.ini
+++ b/config/AcediaAliases.ini
@@ -1,117 +1,18 @@
-[AcediaCore.FeatureAliasSource]
-[AcediaCore:Commands_Feature FeatureAliases]
-Alias="commands"
-Alias="command"
-Alias="comands"
-Alias="comand"
-Alias="cmds"
-Alias="cmd"
-
-[AcediaCore:Avarice_Feature FeatureAliases]
-Alias="Avarice"
-
-[AcediaFixes:FixAmmoSelling_Feature FeatureAliases]
-Alias="FixAmmoSelling"
-Alias="FixSelling"
-Alias="FixAmmo"
-Alias="AmmoSellingFix"
-Alias="SellingFix"
-Alias="AmmoFix"
-Alias="FixAmmoSell"
-Alias="FixSell"
-Alias="AmmoSellFix"
-Alias="SellFix"
-Alias="FixAmmoPrinting"
-Alias="FixPrinting"
-Alias="AmmoPrintingFix"
-Alias="PrintingFix"
-Alias="FixAmmoPrint"
-Alias="FixPrint"
-Alias="AmmoPrintFix"
-Alias="PrintFix"
-
-[AcediaFixes:FixDoshSpam_Feature FeatureAliases]
-Alias="DoshSpamFix"
-Alias="DoshFix"
-Alias="CashSpamFix"
-Alias="CashFix"
-Alias="FixDoshSpamF"
-Alias="FixDosh"
-Alias="FixCashSpam"
-Alias="FixCash"
-
-[AcediaFixes:FixDualiesCost_Feature FeatureAliases]
-Alias="FixDualiesCost"
-Alias="FixDualies"
-Alias="DualiesCostFix"
-Alias="DualiesFix"
-
-[AcediaFixes:FixFFHack_Feature FeatureAliases]
-Alias="FixFFHack"
-Alias="FixFriendlyFireHack"
-Alias="FixFriendFireHack"
-Alias="FFHackFix"
-Alias="FriendlyFireHackFix"
-Alias="FriendFireHackFix"
-
-[AcediaFixes:FixInfiniteNades_Feature FeatureAliases]
-Alias="FixInfiniteNades"
-Alias="FixInfiniteNade"
-Alias="FixInfNades"
-Alias="FixInfNade"
-Alias="FixNades"
-Alias="FixNade"
-Alias="InfiniteNadesFix"
-Alias="InfiniteNadeFix"
-Alias="InfNadesFix"
-Alias="InfNadeFix"
-Alias="NadesFix"
-Alias="NadeFix"
-
-[AcediaFixes:FixInventoryAbuse_Feature FeatureAliases]
-Alias="FixInventoryAbuse"
-Alias="FixInventory"
-Alias="InventoryAbuseFix"
-Alias="InventoryFix"
-
-[AcediaFixes:FixLogSpam_Feature FeatureAliases]
-Alias="FixLogSpam"
-Alias="FixLog"
-Alias="LogSpamFix"
-Alias="LogFix"
-
-[AcediaFixes:FixPipes_Feature FeatureAliases]
-Alias="FixPipes"
-Alias="FixPipe"
-Alias="PipesFix"
-Alias="PipeFix"
-
-[AcediaFixes:FixProjectileFF_Feature FeatureAliases]
-Alias="FixProjectileFriendlyFire"
-Alias="FixProjectileFF"
-Alias="FixProjFriendlyFire"
-Alias="FixProjFF"
-Alias="FixFriendlyFire"
-Alias="FixFF"
-Alias="ProjectileFriendlyFireFix"
-Alias="ProjectileFFFix"
-Alias="ProjFriendlyFireFix"
-Alias="ProjFFFix"
-Alias="FriendlyFireFix"
-Alias="FFFix"
-
-[AcediaFixes:FixSpectatorCrash_Feature FeatureAliases]
-Alias="FixSpectatorCrash"
-Alias="FixSpecCrash"
-Alias="SpectatorCrashFix"
-Alias="SpecCrashFix"
-
-[AcediaFixes:FixZedTimeLags_Feature FeatureAliases]
-Alias="FixZedTimeLags"
-Alias="FixZedTime"
-Alias="FixZTLags"
-Alias="FixZT"
-Alias="ZedTimeLagsFix"
-Alias="ZedTimeFix"
-Alias="ZTLagsFix"
-Alias="ZTFix"
\ No newline at end of file
+; NOTE: You most likely do not want to modify this file, unless you are really
+; sure what you're doing or was told exactly what to do by someone.
+[default Aliases]
+; This feature allows you to configure what alias sources are used for resolving
+; what alias types. Like all Acedia's features, it supports multiple named
+; configs, allowing you to, say, replacve `weaponAliasSoruce` with a different
+; one when running a mod that replaces stock weapons.
+autoEnable=true
+weaponAliasSource=Class'AcediaCore.WeaponAliasSource'
+colorAliasSource=Class'AcediaCore.ColorAliasSource'
+featureAliasSource=Class'AcediaCore.FeatureAliasSource'
+entityAliasSource=Class'AcediaCore.EntityAliasSource'
+; `customSource` records allow you to add custom alias sources into Acedia,
+; that is sources that do not fit either of the standard groups. These sources
+; can then be used by mods to resolve custom aliases using specified `name`.
+; Their names, unlike aliases, are case-sensitive.
+;customSource=(name="custom",source=Class'BlaBla.CustomSource')
+customSource=(name="mock",source=Class'AcediaCore.MockAliasSource')
\ No newline at end of file
diff --git a/config/AcediaAliases_Colors.ini b/config/AcediaAliases_Colors.ini
index 7bb5141..913130d 100644
--- a/config/AcediaAliases_Colors.ini
+++ b/config/AcediaAliases_Colors.ini
@@ -1,3 +1,5 @@
+; This config file allows you to configure color aliases.
+; Remember that aliases are case-insensitive.
[AcediaCore.ColorAliasSource]
; System colors
record=(alias="TextDefault",value="rgb(255,255,255)")
diff --git a/config/AcediaAliases_Entities.ini b/config/AcediaAliases_Entities.ini
index 6a048b3..43f6a98 100644
--- a/config/AcediaAliases_Entities.ini
+++ b/config/AcediaAliases_Entities.ini
@@ -1,3 +1,5 @@
+; This config file allows you to configure entity aliases.
+; Remember that aliases are case-insensitive.
[AcediaCore.EntityAliasSource]
; Standard zeds
[KFChar:ZombieClot_STANDARD EntityAliases]
diff --git a/config/AcediaAliases_Features.ini b/config/AcediaAliases_Features.ini
new file mode 100644
index 0000000..80711b6
--- /dev/null
+++ b/config/AcediaAliases_Features.ini
@@ -0,0 +1,119 @@
+; This config file allows you to configure feature aliases.
+; Remember that aliases are case-insensitive.
+[AcediaCore.FeatureAliasSource]
+[AcediaCore:Commands_Feature FeatureAliases]
+Alias="commands"
+Alias="command"
+Alias="comands"
+Alias="comand"
+Alias="cmds"
+Alias="cmd"
+
+[AcediaCore:Avarice_Feature FeatureAliases]
+Alias="Avarice"
+
+[AcediaFixes:FixAmmoSelling_Feature FeatureAliases]
+Alias="FixAmmoSelling"
+Alias="FixSelling"
+Alias="FixAmmo"
+Alias="AmmoSellingFix"
+Alias="SellingFix"
+Alias="AmmoFix"
+Alias="FixAmmoSell"
+Alias="FixSell"
+Alias="AmmoSellFix"
+Alias="SellFix"
+Alias="FixAmmoPrinting"
+Alias="FixPrinting"
+Alias="AmmoPrintingFix"
+Alias="PrintingFix"
+Alias="FixAmmoPrint"
+Alias="FixPrint"
+Alias="AmmoPrintFix"
+Alias="PrintFix"
+
+[AcediaFixes:FixDoshSpam_Feature FeatureAliases]
+Alias="DoshSpamFix"
+Alias="DoshFix"
+Alias="CashSpamFix"
+Alias="CashFix"
+Alias="FixDoshSpamF"
+Alias="FixDosh"
+Alias="FixCashSpam"
+Alias="FixCash"
+
+[AcediaFixes:FixDualiesCost_Feature FeatureAliases]
+Alias="FixDualiesCost"
+Alias="FixDualies"
+Alias="DualiesCostFix"
+Alias="DualiesFix"
+
+[AcediaFixes:FixFFHack_Feature FeatureAliases]
+Alias="FixFFHack"
+Alias="FixFriendlyFireHack"
+Alias="FixFriendFireHack"
+Alias="FFHackFix"
+Alias="FriendlyFireHackFix"
+Alias="FriendFireHackFix"
+
+[AcediaFixes:FixInfiniteNades_Feature FeatureAliases]
+Alias="FixInfiniteNades"
+Alias="FixInfiniteNade"
+Alias="FixInfNades"
+Alias="FixInfNade"
+Alias="FixNades"
+Alias="FixNade"
+Alias="InfiniteNadesFix"
+Alias="InfiniteNadeFix"
+Alias="InfNadesFix"
+Alias="InfNadeFix"
+Alias="NadesFix"
+Alias="NadeFix"
+
+[AcediaFixes:FixInventoryAbuse_Feature FeatureAliases]
+Alias="FixInventoryAbuse"
+Alias="FixInventory"
+Alias="InventoryAbuseFix"
+Alias="InventoryFix"
+
+[AcediaFixes:FixLogSpam_Feature FeatureAliases]
+Alias="FixLogSpam"
+Alias="FixLog"
+Alias="LogSpamFix"
+Alias="LogFix"
+
+[AcediaFixes:FixPipes_Feature FeatureAliases]
+Alias="FixPipes"
+Alias="FixPipe"
+Alias="PipesFix"
+Alias="PipeFix"
+
+[AcediaFixes:FixProjectileFF_Feature FeatureAliases]
+Alias="FixProjectileFriendlyFire"
+Alias="FixProjectileFF"
+Alias="FixProjFriendlyFire"
+Alias="FixProjFF"
+Alias="FixFriendlyFire"
+Alias="FixFF"
+Alias="ProjectileFriendlyFireFix"
+Alias="ProjectileFFFix"
+Alias="ProjFriendlyFireFix"
+Alias="ProjFFFix"
+Alias="FriendlyFireFix"
+Alias="FFFix"
+
+[AcediaFixes:FixSpectatorCrash_Feature FeatureAliases]
+Alias="FixSpectatorCrash"
+Alias="FixSpecCrash"
+Alias="SpectatorCrashFix"
+Alias="SpecCrashFix"
+
+[AcediaFixes:FixZedTimeLags_Feature FeatureAliases]
+Alias="FixZedTimeLags"
+Alias="FixZedTime"
+Alias="FixZTLags"
+Alias="FixZT"
+Alias="ZedTimeLagsFix"
+Alias="ZedTimeFix"
+Alias="ZTLagsFix"
+Alias="ZTFix"
\ No newline at end of file
diff --git a/config/AcediaAliases_Tests.ini b/config/AcediaAliases_Tests.ini
index a82a0cd..8c687e2 100644
--- a/config/AcediaAliases_Tests.ini
+++ b/config/AcediaAliases_Tests.ini
@@ -6,7 +6,6 @@
[AcediaCore.MockAliasSource]
record=(alias="global",value="value")
record=(alias="question",value="response")
-record=(alias="",value="empty")
record=(alias="also",value="")
[car MockAliases]
Alias="Ford"
diff --git a/config/AcediaAliases_Weapons.ini b/config/AcediaAliases_Weapons.ini
index a0f4855..ea29b70 100644
--- a/config/AcediaAliases_Weapons.ini
+++ b/config/AcediaAliases_Weapons.ini
@@ -1,3 +1,5 @@
+; This config file allows you to configure weapon aliases.
+; Remember that aliases are case-insensitive.
[AcediaCore.WeaponAliasSource]
; Field Medic weapons
[KFMod:MP7MMedicGun WeaponAliases]
@@ -133,44 +135,34 @@ Alias="HuntingShotgun"
Alias="BoomStick"
Alias="Hunting"
[KFMod:KSGShotgun WeaponAliases]
-Alias="HSG-1Shotgun"
Alias="HSG1Shotgun"
Alias="HSGShotgun"
Alias="HSG"
-Alias="KSG-1Shotgun"
Alias="KSG1Shotgun"
Alias="KSGShotgun"
Alias="KSG"
-Alias="HSG-1Shotgun0"
Alias="HSG1Shotgun0"
Alias="HSGShotgun0"
Alias="HSG0"
-Alias="KSG-1Shotgun0"
Alias="KSG1Shotgun0"
Alias="KSGShotgun0"
Alias="KSG0"
[KFMod:NeonKSGShotgun WeaponAliases]
-Alias="HSG-1Shotgun1"
Alias="HSG1Shotgun1"
Alias="HSGShotgun1"
Alias="HSG1"
-Alias="KSG-1Shotgun1"
Alias="KSG1Shotgun1"
Alias="KSGShotgun1"
Alias="KSG1"
-Alias="NeonHSG-1Shotgun"
Alias="NeonHSG1Shotgun"
Alias="NeonHSGShotgun"
Alias="NeonHSG"
-Alias="NeonKSG-1Shotgun"
Alias="NeonKSG1Shotgun"
Alias="NeonKSGShotgun"
Alias="NeonKSG"
-Alias="HSG-1ShotgunNeon"
Alias="HSG1ShotgunNeon"
Alias="HSGShotgunNeon"
Alias="HSGNeon"
-Alias="KSG-1ShotgunNeon"
Alias="KSG1ShotgunNeon"
Alias="KSGShotgunNeon"
Alias="KSGNeon"
@@ -593,13 +585,9 @@ Alias="SteamTomySMG"
Alias="SteamTomy"
Alias="TomySMGSteam"
Alias="TomySteam"
-Alias="Dr.T'sLeadDeliverySystem"
Alias="Dr.TsLeadDeliverySystem"
-Alias="DrT'sLeadDeliverySystem"
Alias="DrTsLeadDeliverySystem"
-Alias="Dr.T'LeadDeliverySystem"
Alias="Dr.TLeadDeliverySystem"
-Alias="DrT'LeadDeliverySystem"
Alias="DrTLeadDeliverySystem"
Alias="DrTDeliverySystem"
Alias="DrTLeadSystem"
diff --git a/config/AcediaSystem.ini b/config/AcediaSystem.ini
index 17d627b..ac5dbc6 100644
--- a/config/AcediaSystem.ini
+++ b/config/AcediaSystem.ini
@@ -119,17 +119,6 @@ levelStamp=true
; centralized manner.
autoEnable=true
-[AcediaCore.AliasService]
-; Changing these allows you to change in what sources `AliasesAPI`
-; looks for weapon and color aliases.
-weaponAliasesSource=Class'WeaponAliasSource'
-colorAliasesSource=Class'ColorAliasSource'
-featureAliasesSource=Class'FeatureAliasSource'
-; How often are different alias-storing objects are allowed to record
-; their updated data into a config.
-; Negative or zero values would be reset to `0.05`.
-saveInterval=0.05
-
[AcediaCore.TestingService]
; Allows you to run tests on server's start up. This option is to help run
; tests quicker during development and should not be used for servers that are
diff --git a/sources/Aliases/AliasService.uc b/sources/Aliases/AliasService.uc
deleted file mode 100644
index 980a2f2..0000000
--- a/sources/Aliases/AliasService.uc
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * Service that handles pending saving of aliases data into configs.
- * Adding aliases into `AliasSource`s causes corresponding configs to update.
- * This service allows to delay and spread config rewrites over time,
- * which should help in case someone dynamically adds a lot of
- * different aliases.
- * Copyright 2020 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 AliasService extends Service
- config(AcediaSystem);
-
-// Objects for which we are yet to write configs
-var private array sourcesPendingToSave;
-var private array aliasesPendingToSave;
-// How often should we do it.
-// Negative or zero values would be reset to `0.05`.
-var public config const float saveInterval;
-
-// To avoid creating yet another object for aliases system we will
-// keep config variable pointing to weapon, color, etc. `AliasSource`
-// subclasses here. It's not the best regarding separation of responsibility,
-// but should make config files less fragmented.
-// Changing these allows you to change in what sources `AliasesAPI`
-// looks for weapon and color aliases.
-var public config const class weaponAliasesSource;
-var public config const class colorAliasesSource;
-var public config const class featureAliasesSource;
-var public config const class entityAliasesSource;
-
-protected simulated function OnLaunch()
-{
- local float actualInterval;
- actualInterval = saveInterval;
- if (actualInterval <= 0)
- {
- actualInterval = 0.05;
- }
- SetTimer(actualInterval, true);
-}
-
-protected simulated function OnShutdown()
-{
- SaveAllPendingObjects();
-}
-
-public simulated final function PendingSaveSource(AliasSource sourceToSave)
-{
- local int i;
- if (sourceToSave == none) return;
- // Starting searching from the end of an array will make situations when
- // we add several aliases to a single source in a row more efficient.
- for (i = sourcesPendingToSave.length - 1;i >= 0; i -= 1) {
- if (sourcesPendingToSave[i] == sourceToSave) return;
- }
- sourcesPendingToSave[sourcesPendingToSave.length] = sourceToSave;
-}
-
-public simulated final function PendingSaveObject(Aliases objectToSave)
-{
- local int i;
- if (objectToSave == none) return;
- // Starting searching from the end of an array will make situations when
- // we add several aliases to a single `Aliases` object in a row
- // more efficient.
- for (i = aliasesPendingToSave.length - 1;i >= 0; i -= 1) {
- if (aliasesPendingToSave[i] == objectToSave) return;
- }
- aliasesPendingToSave[aliasesPendingToSave.length] = objectToSave;
-}
-
-/**
- * Forces saving of the next object (either `AliasSource` or `Aliases`)
- * in queue to the config file.
- *
- * Does not reset the timer until next saving.
- */
-private simulated final function DoSaveNextPendingObject()
-{
- if (sourcesPendingToSave.length > 0)
- {
- if (sourcesPendingToSave[0] != none) {
- sourcesPendingToSave[0].SaveConfig();
- }
- sourcesPendingToSave.Remove(0, 1);
- return;
- }
- if (aliasesPendingToSave.length > 0)
- {
- aliasesPendingToSave[0].SaveOrClear();
- aliasesPendingToSave.Remove(0, 1);
- }
-}
-
-/**
- * Forces saving of all objects (both `AliasSource`s or `Aliases`s) in queue
- * to their config files.
- */
-private simulated final function SaveAllPendingObjects()
-{
- local int i;
- for (i = 0; i < sourcesPendingToSave.length; i += 1) {
- if (sourcesPendingToSave[i] == none) continue;
- sourcesPendingToSave[i].SaveConfig();
- }
- for (i = 0; i < aliasesPendingToSave.length; i += 1) {
- aliasesPendingToSave[i].SaveOrClear();
- }
- sourcesPendingToSave.length = 0;
- aliasesPendingToSave.length = 0;
-}
-
-public simulated function Timer()
-{
- DoSaveNextPendingObject();
-}
-
-defaultproperties
-{
- saveInterval = 0.05
- weaponAliasesSource = class'WeaponAliasSource'
- colorAliasesSource = class'ColorAliasSource'
- featureAliasesSource = class'FeatureAliasSource'
- entityAliasesSource = class'EntityAliasSource'
-}
\ No newline at end of file
diff --git a/sources/Aliases/AliasSource.uc b/sources/Aliases/AliasSource.uc
index 0baff89..340a923 100644
--- a/sources/Aliases/AliasSource.uc
+++ b/sources/Aliases/AliasSource.uc
@@ -1,11 +1,7 @@
/**
- * Aliases allow users to define human-readable and easier to use
- * "synonyms" to some symbol sequences (mainly names of UnrealScript classes).
* This class implements an alias database that stores aliases inside
* standard config ini-files.
- * Several `AliasSource`s are supposed to exist separately, each storing
- * aliases of particular kind: for weapon, zeds, colors, etc..
- * Copyright 2020 - 2021 Anton Tarasenko
+ * Copyright 2020-2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@@ -22,18 +18,9 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class AliasSource extends Singleton
+class AliasSource extends BaseAliasSource
dependson(HashTable)
- config(AcediaAliases);
-
-// (Sub-)class of `Aliases` objects that this `AliasSource` uses to store
-// aliases in per-object-config manner.
-// Leaving this variable `none` will produce an `AliasSource` that can
-// only store aliases in form of `record=(alias="...",value="...")`.
-var public const class aliasesClass;
-// Storage for all objects of `aliasesClass` class in the config.
-// Exists after `OnCreated()` event and is maintained up-to-date at all times.
-var private array loadedAliasObjects;
+ abstract;
// Links alias to a value.
// An array of these structures (without duplicate `alias` records) defines
@@ -46,153 +33,222 @@ struct AliasValuePair
// Aliases data for saving and loading on a disk (ini-file).
// Name is chosen to make configurational files more readable.
var private config array record;
+
+// (Sub-)class of `Aliases` objects that this `AliasSource` uses to store
+// aliases in per-object-config manner.
+// Leaving this variable `none` will produce an `AliasSource` that can
+// only store aliases in form of `record=(alias="...",value="...")`.
+var public const class aliasesClass;
+// Storage for all objects of `aliasesClass` class in the config.
+// Exists after `OnCreated()` event and is maintained up-to-date at all times.
+var private array loadedAliasObjects;
+
// Faster access to value by alias' name.
// It contains same records as `record` array + aliases from
// `loadedAliasObjects` objects when there are no duplicate aliases.
// Otherwise only stores first loaded alias.
var private HashTable aliasHash;
+// Faster access to all aliases, corresponding to a certain value.
+// This `HashTable` stores same data as `aliasHash`, but "in reverse":
+// for each value as a "key" it stored `ArrayList` of corresponding aliases.
+var private HashTable valueHash;
+
+// `true` means that this `AliasSource` is awaiting saving into its config
+var private bool pendingSaveToConfig;
var private LoggerAPI.Definition errIncorrectAliasPair, warnDuplicateAlias;
+var private LoggerAPI.Definition warnInvalidAlias;
// Load and hash all the data `AliasSource` creation.
-protected simulated function OnCreated()
+protected function Constructor()
{
- if (!AssertAliasesClassIsOwnedByThisSource()) {
- Destroy();
+ // If this check fails - caller alias source is fundamentally broken
+ // and requires mod to be fixed
+ if (!ASSERT_AliasesClassIsOwnedByThisSource()) {
return;
}
// Load and hash
loadedAliasObjects = aliasesClass.static.LoadAllObjects();
aliasHash = _.collections.EmptyHashTable();
+ valueHash = _.collections.EmptyHashTable();
HashValidAliasesFromRecord();
HashValidAliasesFromPerObjectConfig();
}
-protected simulated function OnDestroyed()
+protected function Finalizer()
{
loadedAliasObjects.length = 0;
_.memory.Free(aliasHash);
aliasHash = none;
+ if (pendingSaveToConfig) {
+ SaveConfig();
+ }
}
// Ensures that our `Aliases` class is properly linked with this
// source's class. Logs failure otherwise.
-private simulated final function bool AssertAliasesClassIsOwnedByThisSource()
+private function bool ASSERT_AliasesClassIsOwnedByThisSource()
{
if (aliasesClass == none) return true;
if (aliasesClass.default.sourceClass == class) return true;
+
_.logger.Auto(errIncorrectAliasPair).ArgClass(class);
- Destroy();
return false;
}
// Load hashes from `AliasSource`'s config (`record` array)
-private simulated final function HashValidAliasesFromRecord()
+private function HashValidAliasesFromRecord()
{
local int i;
local Text aliasAsText, valueAsText;
+
for (i = 0; i < record.length; i += 1)
{
aliasAsText = _.text.FromString(record[i].alias);
valueAsText = _.text.FromString(record[i].value);
- InsertAlias(aliasAsText, valueAsText);
+ InsertAliasIntoHash(aliasAsText, valueAsText);
aliasAsText.FreeSelf();
valueAsText.FreeSelf();
}
}
// Load hashes from `Aliases` objects' config
-private simulated final function HashValidAliasesFromPerObjectConfig()
+private function HashValidAliasesFromPerObjectConfig()
{
local int i, j;
local Text nextValue;
local array valueAliases;
+
for (i = 0; i < loadedAliasObjects.length; i += 1)
{
nextValue = loadedAliasObjects[i].GetValue();
valueAliases = loadedAliasObjects[i].GetAliases();
for (j = 0; j < valueAliases.length; j += 1) {
- InsertAlias(valueAliases[j], nextValue);
+ InsertAliasIntoHash(valueAliases[j], nextValue);
}
nextValue.FreeSelf();
_.memory.FreeMany(valueAliases);
}
}
+public static function bool AreValuesCaseSensitive()
+{
+ // Almost all built-in aliases are aliases to class names (or templates)
+ // and the rest are colors. Both are case-insensitive, so returning `false`
+ // is a good default implementation. Child classes can just change this
+ // value, if they need.
+ return false;
+}
+
+public function array GetAliases(BaseText value)
+{
+ local int i;
+ local Text storedValue;
+ local ArrayList aliasesArray;
+ local array result;
+
+ storedValue = NormalizeValue(value);
+ aliasesArray = valueHash.GetArrayList(storedValue);
+ storedValue.FreeSelf();
+ if (aliasesArray == none) {
+ return result;
+ }
+ for (i = 0; i < aliasesArray.GetLength(); i += 1) {
+ result[result.length] = aliasesArray.GetText(i);
+ }
+ return result;
+}
+
+// "Normalizes" value:
+// 1. Converts it into lower case if `AreValuesCaseSensitive()` returns
+// `true`;
+// 2. Converts in into `Text` in case passed value is `MutableText`, so
+// that hash table is actually usable.
+private function Text NormalizeValue(BaseText value)
+{
+ if (value == none) {
+ return none;
+ }
+ if (AreValuesCaseSensitive()) {
+ return value.Copy();
+ }
+ return value.LowerCopy();
+}
+
// Inserts alias into `aliasHash`, cleaning previous keys/values in case
// they already exist.
// Takes care of lower case conversion to store aliases in `aliasHash`
-// in a case-insensitive way.
-private simulated final function InsertAlias(BaseText alias, BaseText value)
+// in a case-insensitive way. Depending on `AreValuesCaseSensitive()`, can also
+// convert values to lower case.
+private function InsertAliasIntoHash(BaseText alias, BaseText value)
{
- local Text aliasLowerCaseCopy;
- local HashTable.Entry hashEntry;
+ local Text storedAlias;
+ local Text storedValue;
+ local Text existingValue;
+ local ArrayList valueAliases;
+
if (alias == none) return;
if (value == none) return;
- aliasLowerCaseCopy = alias.LowerCopy();
- hashEntry = aliasHash.TakeEntry(aliasLowerCaseCopy);
- if (hashEntry.value != none) {
- LogDuplicateAliasWarning(alias, Text(hashEntry.value));
+
+ if (!alias.IsValidName())
+ {
+ _.logger.Auto(warnInvalidAlias)
+ .ArgClass(class)
+ .Arg(alias.Copy());
+ return;
}
- _.memory.Free(hashEntry.key);
- _.memory.Free(hashEntry.value);
- aliasHash.SetItem(aliasLowerCaseCopy, value);
- aliasLowerCaseCopy.FreeSelf();
+ storedAlias = alias.LowerCopy();
+ existingValue = aliasHash.GetText(storedAlias);
+ if (aliasHash.HasKey(storedAlias))
+ {
+ _.logger.Auto(warnDuplicateAlias)
+ .ArgClass(class)
+ .Arg(alias.Copy())
+ .Arg(existingValue);
+ }
+ _.memory.Free(existingValue);
+ storedValue = NormalizeValue(value);
+ // Add to `aliasHash`: alias -> value
+ aliasHash.SetItem(storedAlias, storedValue);
+ // Add to `valueHash`: value -> alias
+ valueAliases = valueHash.GetArrayList(storedValue);
+ if (valueAliases == none) {
+ valueAliases = _.collections.EmptyArrayList();
+ }
+ valueAliases.AddItem(storedAlias);
+ valueHash.SetItem(storedValue, valueAliases);
+ // Clean up
+ storedAlias.FreeSelf();
+ storedValue.FreeSelf();
}
-/**
- * Checks if given alias is present in caller `AliasSource`.
- *
- * @param alias Alias to check, case-insensitive.
- * @return `true` if present, `false` otherwise.
- */
-public simulated function bool HasAlias(BaseText alias)
+public function bool HasAlias(BaseText alias)
{
local bool result;
- local Text lowerCaseAlias;
+ local Text storedAlias;
+
if (alias == none) {
return false;
}
- lowerCaseAlias = alias.LowerCopy();
- result = aliasHash.HasKey(lowerCaseAlias);
- lowerCaseAlias.FreeSelf();
+ storedAlias = alias.LowerCopy();
+ result = aliasHash.HasKey(storedAlias);
+ storedAlias.FreeSelf();
return result;
}
-/**
- * Return value stored for the given alias in caller `AliasSource`
- * (as well as it's `Aliases` objects).
- *
- * @param alias Alias, for which method will attempt to return
- * a value. Case-insensitive. If given `alias` starts with "$" character -
- * that character will be removed before resolving that alias.
- * @param copyOnFailure Whether method should return copy of original
- * `alias` value in case caller source did not have any records
- * corresponding to `alias`.
- * @return If look up was successful - value, associated with the given
- * alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
- * flag: `copyOnFailure == false` means method will return `none`
- * and `copyOnFailure == true` means method will return `alias.Copy()`.
- * If `alias == none` method always returns `none`.
- */
-public simulated function Text Resolve(
+public function Text Resolve(
BaseText alias,
optional bool copyOnFailure)
{
local Text result;
- local Text lowerCaseAlias;
+ local Text storedAlias;
+
if (alias == none) {
return none;
}
- // Automatically get rid of "$" prefix, if present
- if (alias.StartsWith(P("$"))) {
- lowerCaseAlias = alias.LowerCopy(1);
- }
- else {
- lowerCaseAlias = alias.LowerCopy();
- }
- result = Text(aliasHash.GetItem(lowerCaseAlias));
- lowerCaseAlias.FreeSelf();
+ storedAlias = alias.LowerCopy();
+ result = aliasHash.GetText(storedAlias);
+ storedAlias.FreeSelf();
if (result != none) {
return result;
}
@@ -202,99 +258,90 @@ public simulated function Text Resolve(
return none;
}
-/**
- * Adds another alias to the caller `AliasSource`.
- * If alias with the same name as `aliasToAdd` already exists, -
- * method overwrites it.
- *
- * Can fail iff `aliasToAdd` is an invalid alias or `aliasValue == none`.
- *
- * When adding alias to an object (`saveInObject == true`) alias `aliasToAdd`
- * will be altered by changing any ':' inside it into a '.'.
- * This is a necessary measure to allow storing class names in
- * config files via per-object-config.
- *
- * NOTE: This call will cause update of an ini-file. That update can be
- * slightly delayed, so do not make assumptions about it's immediacy.
- *
- * NOTE #2: Removing alias would require this method to go through the
- * whole `AliasSource` to remove possible duplicates.
- * This means that unless you can guarantee that there is no duplicates, -
- * performing a lot of alias additions during run-time can be costly.
- *
- * @param aliasToAdd Alias that you want to add to caller source.
- * Alias names are case-insensitive.
- * @param aliasValue Intended value of this alias.
- * @param saveInObject Setting this to `true` will make `AliasSource` save
- * given alias in per-object-config storage, while keeping it at default
- * `false` will just add alias to the `record=` storage.
- * If caller `AliasSource` does not support per-object-config storage, -
- * this flag will be ignores.
- * @return `true` if alias was added and `false` otherwise (alias was invalid).
- */
-public simulated final function bool AddAlias(
- Text aliasToAdd,
- Text aliasValue,
- optional bool saveInObject)
+public function bool AddAlias(BaseText aliasToAdd, BaseText aliasValue)
{
- local Text lowerCaseAlias;
- local AliasValuePair newPair;
- if (aliasToAdd == none) return false;
- if (aliasValue == none) return false;
+ local Text storedAlias;
- lowerCaseAlias = aliasToAdd.LowerCopy();
- if (aliasHash.HasKey(lowerCaseAlias)) {
+ if (aliasToAdd == none) return false;
+ if (aliasValue == none) return false;
+ if (!aliasToAdd.IsValidName()) return false;
+
+ // Check if alias already exists and if yes - remove it
+ storedAlias = aliasToAdd.LowerCopy();
+ if (aliasHash.HasKey(storedAlias)) {
RemoveAlias(aliasToAdd);
}
- // Save
- if (saveInObject) {
- GetAliasesObjectWithValue(aliasValue).AddAlias(aliasToAdd);
+ storedAlias.FreeSelf();
+ // Add alias-value pair
+ AddToConfigRecords(aliasToAdd.ToString(), aliasValue.ToString());
+ InsertAliasIntoHash(aliasToAdd, aliasValue);
+ return true;
+}
+
+public function bool RemoveAlias(BaseText aliasToRemove)
+{
+ local Text storedAlias, storedValue;
+ local ArrayList valueAliases;
+
+ if (aliasToRemove == none) return false;
+ if (!aliasToRemove.IsValidName()) return false;
+
+ storedAlias = aliasToRemove.LowerCopy();
+ storedValue = aliasHash.GetText(storedAlias);
+ if (storedValue == none)
+ {
+ storedAlias.FreeSelf();
+ return false;
+ }
+ aliasHash.RemoveItem(aliasToRemove);
+ // Since we've found `storedValue`, this couldn't possibly be `none` if
+ // "same data invariant" is preserved (see their declaration)
+ valueAliases = valueHash.GetArrayList(storedValue);
+ if (valueAliases != none) {
+ valueAliases.RemoveItem(storedAlias, true);
}
- else
+ if (valueAliases != none && valueAliases.GetLength() <= 0)
{
- newPair.alias = aliasToAdd.ToString();
- newPair.value = aliasValue.ToString();
- record[record.length] = newPair;
+ valueHash.SetItem(storedValue, none);
+ valueAliases = none;
}
- aliasHash.SetItem(lowerCaseAlias, aliasValue);
- _.memory.Free(lowerCaseAlias);
- _.memory.Free(aliasValue);
- AliasService(class'AliasService'.static.Require()).PendingSaveSource(self);
+ _.memory.Free(valueAliases);
+ RemoveFromConfigRecords(aliasToRemove.ToString());
return true;
}
-/**
- * Removes alias (all records with it, in case of duplicates) from
- * the caller `AliasSource`.
- *
- * Cannot fail.
- *
- * NOTE: This call will cause update of an ini-file. That update can be
- * slightly delayed, so do not make assumptions about it's immediacy.
- *
- * NOTE #2: removing alias requires this method to go through the
- * whole `AliasSource` to remove possible duplicates, which can make
- * performing a lot of alias removal during run-time costly.
- *
- * @param aliasToRemove Alias that you want to remove from caller source.
- */
-public simulated final function RemoveAlias(BaseText aliasToRemove)
+// Takes `string`s that represents alias to remove in proper case (lower for
+// aliases and for values it depends on the caller source's settings):
+// aliases are supposed to be ASCII, so `string` should handle it and its
+// comparison just fine
+private function AddToConfigRecords(string alias, string value)
{
- local int i;
- local bool isMatchingRecord;
- local bool removedAliasFromRecord;
- local HashTable.Entry hashEntry;
- if (aliasToRemove == none) {
- return;
+ local AliasValuePair newPair;
+
+ newPair.alias = alias;
+ newPair.value = value;
+ record[record.length] = newPair;
+ // Request saving
+ if (!pendingSaveToConfig)
+ {
+ pendingSaveToConfig = true;
+ _.scheduler.RequestDiskAccess(self).connect = SaveSelf;
}
- hashEntry = aliasHash.TakeEntry(aliasToRemove);
- _.memory.Free(hashEntry.key);
- _.memory.Free(hashEntry.value);
+}
+
+// Takes `string` that represents alias to remove in lower case: aliases are
+// supposed to be ASCII, so `string` should handle it and its comparison just
+// fine
+private function RemoveFromConfigRecords(string aliasToRemove)
+{
+ local int i;
+ local bool removedAliasFromRecord;
+
+ // Aliases are supposed to be ASCII, so `string` should handle it and its
+ // comparison just fine
while (i < record.length)
{
- isMatchingRecord = aliasToRemove
- .CompareToString(record[i].alias, SCASE_INSENSITIVE);
- if (isMatchingRecord)
+ if (aliasToRemove ~= record[i].alias)
{
record.Remove(i, 1);
removedAliasFromRecord = true;
@@ -303,46 +350,24 @@ public simulated final function RemoveAlias(BaseText aliasToRemove)
i += 1;
}
}
+ // Since admins can fuck up and add duplicate aliases, we need to
+ // thoroughly check every alias object
for (i = 0; i < loadedAliasObjects.length; i += 1) {
- loadedAliasObjects[i].RemoveAlias(aliasToRemove);
+ loadedAliasObjects[i].RemoveAlias_S(aliasToRemove);
}
- if (removedAliasFromRecord)
+ // Alias objects can request disk access themselves, so only record if
+ // needed for the record
+ if (removedAliasFromRecord && !pendingSaveToConfig)
{
- AliasService(class'AliasService'.static.Require())
- .PendingSaveSource(self);
+ pendingSaveToConfig = true;
+ _.scheduler.RequestDiskAccess(self).connect = SaveSelf;
}
}
-private simulated final function LogDuplicateAliasWarning(
- BaseText alias,
- BaseText existingValue)
+private function SaveSelf()
{
- _.logger.Auto(warnDuplicateAlias)
- .ArgClass(class)
- .Arg(alias.Copy())
- .Arg(existingValue.Copy());
-}
-
-// Tries to find a loaded `Aliases` config object that stores aliases for
-// the given value. If such object does not exists - creates a new one.
-// Assumes `value != none`.
-private simulated final function Aliases GetAliasesObjectWithValue(
- BaseText value)
-{
- local int i;
- local Text nextValue;
- local Aliases newAliasesObject;
- for (i = 0; i < loadedAliasObjects.length; i += 1)
- {
- nextValue = loadedAliasObjects[i].GetValue();
- if (value.Compare(nextValue)) {
- return loadedAliasObjects[i];
- }
- _.memory.Free(nextValue);
- }
- newAliasesObject = aliasesClass.static.LoadObject(value);
- loadedAliasObjects[loadedAliasObjects.length] = newAliasesObject;
- return newAliasesObject;
+ pendingSaveToConfig = false;
+ SaveConfig();
}
defaultproperties
@@ -351,4 +376,5 @@ defaultproperties
aliasesClass = class'Aliases'
errIncorrectAliasPair = (l=LOG_Error,m="`AliasSource`-`Aliases` class pair is incorrectly setup for source `%1`. Omitting it.")
warnDuplicateAlias = (l=LOG_Warning,m="Alias source `%1` has duplicate record for alias \"%2\". This is likely due to an erroneous config. \"%3\" value will be used.")
+ warnInvalidAlias = (l=LOG_Warning,m="Alias source `%1` has record with invalid alias \"%2\". This is likely due to an erroneous config. This alias will be discarded.")
}
\ No newline at end of file
diff --git a/sources/Aliases/Aliases.uc b/sources/Aliases/Aliases.uc
index 58014f4..f1bd8f2 100644
--- a/sources/Aliases/Aliases.uc
+++ b/sources/Aliases/Aliases.uc
@@ -1,15 +1,6 @@
/**
- * This is a simple helper object for `AliasSource` that can store
- * an array of aliases in config files in a per-object-config manner.
- * One `Aliases` object can store several aliases for a single value.
- * It is recommended that you do not try to access these objects directly.
- * Class name `Aliases` is chosen to make configuration files
- * more readable.
- * It's only interesting function is storing '.'s as ':' in it's config,
- * which is necessary to allow storing aliases for class names via
- * these objects (since UnrealScript's cannot handle '.'s in object's names
- * in it's configs).
- * Copyright 2019 - 2021 Anton Tarasenko
+ * Config object for `Aliases_Feature`.
+ * Copyright 2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@@ -26,184 +17,118 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class Aliases extends AcediaObject
- perObjectConfig
+class Aliases extends FeatureConfig
+ perobjectconfig
config(AcediaAliases);
-// Name of the configurational file (without extension) where
-// this `AliasSource`'s data will be stored.
-var protected const string configName;
-
-// Link to the `AliasSource` that uses `Aliases` objects of this class.
-// To ensure that any `Aliases` sub-class only belongs to one `AliasSource`.
-var public const class sourceClass;
-
-// Aliases, recorded by this `Aliases` object that all mean the same value,
-// defined by this object's name `string(self.name)`.
-var protected config array alias;
-
-// Since:
-// 1. '.'s in values are converted into ':' for storage purposes;
-// 2. We have to store values in `string` to make use of config files.
-// we need methods to convert between "storage" (`string`)
-// and "actual" (`Text`) value version.
-// `ToStorageVersion()` and `ToActualVersion()` do that.
-private final static function string ToStorageVersion(BaseText actualValue)
-{
- return Repl(actualValue.ToString(), ".", ":");
-}
-
-// See comment to `ToStorageVersion()`.
-private final static function Text ToActualVersion(string storageValue)
+struct CustomSourceRecord
{
- return __().text.FromString(Repl(storageValue, ":", "."));
-}
+ var string name;
+ var class source;
+};
-/**
- * Loads all `Aliases` objects from their config file
- * (defined in paired `AliasSource` class).
- *
- * @return Array of all `Aliases` objects, loaded from their config file.
- */
-public static final function array LoadAllObjects()
-{
- local int i;
- local array objectNames;
- local array loadedAliasObjects;
- objectNames = GetPerObjectNames(default.configName,
- string(default.class.name), MaxInt);
- for (i = 0; i < objectNames.length; i += 1) {
- loadedAliasObjects[i] = LoadObjectByName(objectNames[i]);
- }
- return loadedAliasObjects;
-}
+var public config class weaponAliasSource;
+var public config class colorAliasSource;
+var public config class featureAliasSource;
+var public config class entityAliasSource;
+var public config array customSource;
-// Loads a new `Aliases` object by it's given name (`objectName`).
-private static final function Aliases LoadObjectByName(string objectName)
+protected function HashTable ToData()
{
- local Aliases result;
- // Since `MemoryAPI` for now does not support specifying names
- // to created objects - do some manual dark magic and
- // initialize this shit ourselves
- result = new(none, objectName) default.class;
- result._constructor();
- return result;
-}
+ local int i;
+ local Text nextKey;
+ local HashTable data, otherSourcesData;
-/**
- * Loads a new `Aliases` object based on the value (`aliasesValue`)
- * of it's aliases.
- *
- * @param aliasesValue Value that aliases in this `Aliases` object will
- * correspond to.
- * @return Instance of `Aliases` object with a given name.
- */
-public static final function Aliases LoadObject(BaseText aliasesValue)
-{
- if (aliasesValue != none) {
- return LoadObjectByName(ToStorageVersion(aliasesValue));
+ data = __().collections.EmptyHashTable();
+ // Add named aliases
+ data.SetString(P("weapon"), string(weaponAliasSource));
+ data.SetString(P("color"), string(colorAliasSource));
+ data.SetString(P("feature"), string(featureAliasSource));
+ data.SetString(P("entity"), string(entityAliasSource));
+ // Add the rest
+ otherSourcesData = __().collections.EmptyHashTable();
+ for (i = 0; i < customSource.length; i += 1)
+ {
+ nextKey = _.text.FromString(customSource[i].name);
+ otherSourcesData.SetString(nextKey, string(customSource[i].source));
+ nextKey.FreeSelf();
}
- return none;
+ data.SetItem(P("other"), otherSourcesData);
+ otherSourcesData.FreeSelf();
+ return data;
}
-/**
- * Returns value that caller's `Aliases` object's aliases point to.
- *
- * @return Value, stored by this object.
- */
-public final function Text GetValue()
+protected function FromData(HashTable source)
{
- return ToActualVersion(string(self.name));
-}
+ local HashTable otherSourcesData;
-/**
- * Returns array of aliases that caller `Aliases` tells us point to it's value.
- *
- * @return Array of all aliases, stored by caller `Aliases` object.
- */
-public final function array GetAliases()
-{
- local int i;
- local array textAliases;
- for (i = 0; i < alias.length; i += 1) {
- textAliases[i] = _.text.FromString(alias[i]);
+ if (source == none) {
+ return;
}
- return textAliases;
-}
-
-/**
- * [For inner use by `AliasSource`] Adds new alias to this object.
- *
- * Does no duplicates checks through for it's `AliasSource` and
- * neither does it update relevant `AliasHash`,
- * but will prevent adding duplicate records inside it's own storage.
- *
- * @param aliasToAdd Alias to add to caller `Aliases` object.
- * If `none`, method will do nothing.
- */
-public final function AddAlias(BaseText aliasToAdd)
-{
- local int i;
- if (aliasToAdd == none) return;
- for (i = 0; i < alias.length; i += 1)
+ // We cast `class` into `string`
+ // (e.g. `string(class'AcediaAliases_Weapons')`)
+ // instead of writing full name of the class so that code is independent
+ // from this package's name, making it easier to change later
+ weaponAliasSource = class(_.memory.LoadClass_S(
+ source.GetString(P("weapon"), string(class'WeaponAliasSource'))));
+ colorAliasSource = class(_.memory.LoadClass_S(
+ source.GetString(P("color"), string(class'ColorAliasSource'))));
+ featureAliasSource = class(_.memory.LoadClass_S(
+ source.GetString(P("feature"), string(class'FeatureAliasSource'))));
+ entityAliasSource = class(_.memory.LoadClass_S(
+ source.GetString(P("entity"), string(class'EntityAliasSource'))));
+ otherSourcesData = source.GetHashTable(P("other"));
+ if (otherSourcesData != none)
{
- if (aliasToAdd.CompareToString(alias[i], SCASE_INSENSITIVE)) {
- return;
- }
+ ReadOtherSources(otherSourcesData);
+ otherSourcesData.FreeSelf();
}
- alias[alias.length] = aliasToAdd.ToString();
- AliasService(class'AliasService'.static.Require())
- .PendingSaveObject(self);
}
-/**
- * [For inner use by `AliasSource`] Removes alias from this object.
- *
- * Does not update relevant `AliasHash`.
- *
- * Will prevent adding duplicate records inside it's own storage.
- *
- * @param aliasToRemove Alias to remove from caller `Aliases` object.
- */
-public final function RemoveAlias(BaseText aliasToRemove)
+// Doesn't check whether `otherSources` is `none`
+protected function ReadOtherSources(HashTable otherSourcesData)
{
- local int i;
- local bool removedAlias;
- if (aliasToRemove == none) return;
- while (i < alias.length)
+ local CustomSourceRecord newRecord;
+ local BaseText keyAsText, valueAsText;
+ local AcediaObject key, value;
+ local HashTableIterator iter;
+
+ customSource.length = 0;
+ iter = HashTableIterator(otherSourcesData.Iterate().LeaveOnlyNotNone());
+ for (iter = iter; !iter.HasFinished(); iter.Next())
{
- if (aliasToRemove.CompareToString(alias[i], SCASE_INSENSITIVE))
+ key = iter.GetKey();
+ value = iter.GetKey();
+ keyAsText = BaseText(key);
+ valueAsText = BaseText(value);
+ if (keyAsText != none && valueAsText != none)
{
- alias.Remove(i, 1);
- removedAlias = true;
- }
- else {
- i += 1;
+ newRecord.name = keyAsText.ToString();
+ newRecord.source = class(
+ _.memory.LoadClass_S(valueAsText.ToString()));
+ if (newRecord.source != none) {
+ customSource[customSource.length] = newRecord;
+ }
}
- }
- if (removedAlias)
- {
- AliasService(class'AliasService'.static.Require())
- .PendingSaveObject(self);
+ _.memory.Free(key);
+ _.memory.Free(value);
}
}
-/**
- * If this object still has any alias records, - forces a rewrite of it's data
- * into the config file, otherwise - removes it's record entirely.
- */
-public final function SaveOrClear()
+protected function DefaultIt()
{
- if (alias.length <= 0) {
- ClearConfig();
- }
- else {
- SaveConfig();
- }
+ customSource.length = 0;
+ weaponAliasSource = class'WeaponAliasSource';
+ colorAliasSource = class'ColorAliasSource';
+ featureAliasSource = class'FeatureAliasSource';
+ entityAliasSource = class'EntityAliasSource';
}
defaultproperties
{
- sourceClass = class'AliasSource'
- configName = "AcediaAliases"
+ configName = "AcediaAliases"
+ weaponAliasSource = class'WeaponAliasSource'
+ colorAliasSource = class'ColorAliasSource'
+ featureAliasSource = class'FeatureAliasSource'
+ entityAliasSource = class'EntityAliasSource'
}
\ No newline at end of file
diff --git a/sources/Aliases/AliasesAPI.uc b/sources/Aliases/AliasesAPI.uc
index 8b63dd4..e58261d 100644
--- a/sources/Aliases/AliasesAPI.uc
+++ b/sources/Aliases/AliasesAPI.uc
@@ -1,6 +1,6 @@
/**
* Provides convenient access to Aliases-related functions.
- * Copyright 2020 - 2021 Anton Tarasenko
+ * Copyright 2020-2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@@ -20,154 +20,169 @@
class AliasesAPI extends AcediaObject
dependson(LoggerAPI);
-var private LoggerAPI.Definition noWeaponAliasSource, invalidWeaponAliasSource;
-var private LoggerAPI.Definition noColorAliasSource, invalidColorAliasSource;
-var private LoggerAPI.Definition noFeatureAliasSource, invalidFeatureAliasSource;
-var private LoggerAPI.Definition noEntityAliasSource, invalidEntityAliasSource;
+// To avoid bothering with fetching `Aliases_Feature` each time we need to
+// access an alias source, we save all the basic ones separately and
+// `Aliases_Feature` can simply trigger their updates whenever necessary via
+// `AliasesAPI._reloadSources()` function.
+var private BaseAliasSource weaponAliasSource;
+var private BaseAliasSource colorAliasSource;
+var private BaseAliasSource featureAliasSource;
+var private BaseAliasSource entityAliasSource;
+
+public function _reloadSources()
+{
+ local Aliases_Feature feature;
+
+ _.memory.Free(weaponAliasSource);
+ _.memory.Free(colorAliasSource);
+ _.memory.Free(featureAliasSource);
+ _.memory.Free(entityAliasSource);
+ weaponAliasSource = none;
+ colorAliasSource = none;
+ featureAliasSource = none;
+ entityAliasSource = none;
+ feature = Aliases_Feature(
+ class'Aliases_Feature'.static.GetEnabledInstance());
+ if (feature == none) {
+ return;
+ }
+ weaponAliasSource = feature.GetWeaponSource();
+ colorAliasSource = feature.GetColorSource();
+ featureAliasSource = feature.GetFeatureSource();
+ entityAliasSource = feature.GetEntitySource();
+ _.memory.Free(feature);
+}
/**
- * Provides an easier access to the instance of the `AliasSource` of
- * the given class.
+ * Provides an easier access to the instance of the custom `BaseAliasSource`
+ * with a given name `sourceName`.
*
- * Can fail if `customSourceClass` is incorrectly defined.
+ * Custom alias sources can be added manually by the admin through the
+ * `Aliases_Feature` config file..
*
- * @param customSourceClass Class of the source we want.
- * @return Instance of the requested `AliasSource`,
- * `none` if `customSourceClass` is incorrectly defined.
+ * @param sourceName Name that alias source was added as to
+ * `Aliases_Feature`.
+ * @return Instance of the requested `BaseAliasSource`,
+ * `none` if `sourceName` is `none`, does not refer to any alias source
+ * or `Aliases_Feature` is disabled.
*/
-public final function AliasSource GetCustomSource(
- class customSourceClass)
+public final function BaseAliasSource GetCustomSource(BaseText sourceName)
{
- return AliasSource(customSourceClass.static.GetInstance(true));
+ local Aliases_Feature feature;
+
+ if (sourceName == none) {
+ return none;
+ }
+ feature =
+ Aliases_Feature(class'Aliases_Feature'.static.GetEnabledInstance());
+ if (feature != none) {
+ return feature.GetCustomSource(sourceName);
+ }
+ return none;
}
/**
- * Returns `AliasSource` that is designated in configuration files as
+ * Returns `BaseAliasSource` that is designated in configuration files as
* a source for weapon aliases.
*
- * NOTE: while by default weapon aliases source will contain only weapon
- * aliases, you should not assume that. Acedia allows admins to store all
- * the aliases in the same config.
- *
- * @return Reference to the `AliasSource` that contains weapon aliases.
+ * @return Reference to the `BaseAliasSource` that contains weapon aliases.
* Can return `none` if no source for weapons was configured or
* the configured source is incorrectly defined.
*/
-public final function AliasSource GetWeaponSource()
+public final function BaseAliasSource GetWeaponSource()
{
- local AliasSource weaponSource;
- local class sourceClass;
- sourceClass = class'AliasService'.default.weaponAliasesSource;
- if (sourceClass == none)
- {
- _.logger.Auto(noWeaponAliasSource);
- return none;
+ if (weaponAliasSource != none) {
+ weaponAliasSource.NewRef();
}
- weaponSource = AliasSource(sourceClass.static.GetInstance(true));
- if (weaponSource == none)
- {
- _.logger.Auto(invalidWeaponAliasSource).ArgClass(sourceClass);
- return none;
- }
- return weaponSource;
+ return weaponAliasSource;
}
/**
- * Returns `AliasSource` that is designated in configuration files as
+ * Returns `BaseAliasSource` that is designated in configuration files as
* a source for color aliases.
*
- * NOTE: while by default color aliases source will contain only color aliases,
- * you should not assume that. Acedia allows admins to store all the aliases
- * in the same config.
*
- * @return Reference to the `AliasSource` that contains color aliases.
+ * @return Reference to the `BaseAliasSource` that contains color aliases.
* Can return `none` if no source for colors was configured or
* the configured source is incorrectly defined.
*/
-public final function AliasSource GetColorSource()
+public final function BaseAliasSource GetColorSource()
{
- local AliasSource colorSource;
- local class sourceClass;
- sourceClass = class'AliasService'.default.colorAliasesSource;
- if (sourceClass == none)
- {
- _.logger.Auto(noColorAliasSource);
- return none;
+ if (colorAliasSource != none) {
+ colorAliasSource.NewRef();
}
- colorSource = AliasSource(sourceClass.static.GetInstance(true));
- if (colorSource == none)
- {
- _.logger.Auto(invalidColorAliasSource).ArgClass(sourceClass);
- return none;
- }
- return colorSource;
+ return colorAliasSource;
}
/**
- * Returns `AliasSource` that is designated in configuration files as
+ * Returns `BaseAliasSource` that is designated in configuration files as
* a source for feature aliases.
*
- * NOTE: while by default feature aliases source will contain only feature
- * aliases, you should not assume that. Acedia allows admins to store all the
- * aliases in the same config.
*
- * @return Reference to the `AliasSource` that contains feature aliases.
+ * @return Reference to the `BaseAliasSource` that contains feature aliases.
* Can return `none` if no source for features was configured or
* the configured source is incorrectly defined.
*/
-public final function AliasSource GetFeatureSource()
+public final function BaseAliasSource GetFeatureSource()
{
- local AliasSource featureSource;
- local class sourceClass;
- sourceClass = class'AliasService'.default.featureAliasesSource;
- if (sourceClass == none)
- {
- _.logger.Auto(noFeatureAliasSource);
- return none;
+ if (featureAliasSource != none) {
+ featureAliasSource.NewRef();
}
- featureSource = AliasSource(sourceClass.static.GetInstance(true));
- if (featureSource == none)
- {
- _.logger.Auto(invalidFeatureAliasSource).ArgClass(sourceClass);
- return none;
- }
- return featureSource;
+ return featureAliasSource;
}
/**
- * Returns `AliasSource` that is designated in configuration files as
+ * Returns `BaseAliasSource` that is designated in configuration files as
* a source for entity aliases.
*
- * NOTE: while by default entity aliases source will contain only entity
- * aliases, you should not assume that. Acedia allows admins to store all the
- * aliases in the same config.
*
- * @return Reference to the `AliasSource` that contains entity aliases.
+ * @return Reference to the `BaseAliasSource` that contains entity aliases.
* Can return `none` if no source for entities was configured or
* the configured source is incorrectly defined.
*/
-public final function AliasSource GetEntitySource()
+public final function BaseAliasSource GetEntitySource()
{
- local AliasSource entitySource;
- local class sourceClass;
- sourceClass = class'AliasService'.default.entityAliasesSource;
- if (sourceClass == none)
- {
- _.logger.Auto(noEntityAliasSource);
- return none;
+ if (entityAliasSource != none) {
+ entityAliasSource.NewRef();
}
- entitySource = AliasSource(sourceClass.static.GetInstance(true));
- if (entitySource == none)
- {
- _.logger.Auto(invalidEntityAliasSource).ArgClass(sourceClass);
+ return entityAliasSource;
+}
+
+private final function Text ResolveWithSource(
+ BaseText alias,
+ BaseAliasSource source,
+ optional bool copyOnFailure)
+{
+ local Text result;
+ local Text trimmedAlias;
+
+ if (alias == none) {
return none;
}
- return entitySource;
+ if (alias.StartsWith(P("$"))) {
+ trimmedAlias = alias.Copy(1);
+ }
+ else {
+ trimmedAlias = alias.Copy();
+ }
+ if (source != none) {
+ result = source.Resolve(trimmedAlias, copyOnFailure);
+ }
+ else if (copyOnFailure) {
+ result = trimmedAlias.Copy();
+ }
+ trimmedAlias.FreeSelf();
+ return result;
}
/**
- * Tries to look up a value stored for given alias in an `AliasSource`
- * configured to store weapon aliases. Returns `none` on failure.
+ * Tries to look up a value stored for given alias in an `BaseAliasSource`
+ * configured to store weapon aliases.
+ *
+ * In Acedia aliases are typically prefixed with '$' to indicate that user
+ * means to enter alias. This method is able to handle both aliases with and
+ * without that prefix. This does not lead to conflicts, because '$' is cannot
+ * be a valid part of any alias.
*
* Lookup of alias can fail if either alias does not exist in weapon alias
* source or weapon alias source itself does not exist
@@ -179,7 +194,8 @@ public final function AliasSource GetEntitySource()
* look up a value. Case-insensitive.
* @param copyOnFailure Whether method should return copy of original
* `alias` value in case caller source did not have any records
- * corresponding to `alias`.
+ * corresponding to `alias`. If `alias` was specified with '$' prefix -
+ * it will be discarded.
* @return If look up was successful - value, associated with the given
* alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
* flag: `copyOnFailure == false` means method will return `none`
@@ -190,17 +206,17 @@ public final function Text ResolveWeapon(
BaseText alias,
optional bool copyOnFailure)
{
- local AliasSource source;
- source = GetWeaponSource();
- if (source != none) {
- return source.Resolve(alias, copyOnFailure);
- }
- return none;
+ return ResolveWithSource(alias, weaponAliasSource, copyOnFailure);
}
/**
- * Tries to look up a value stored for given alias in an `AliasSource`
- * configured to store color aliases. Reports error on failure.
+ * Tries to look up a value stored for given alias in an `BaseAliasSource`
+ * configured to store color aliases.
+ *
+ * In Acedia aliases are typically prefixed with '$' to indicate that user
+ * means to enter alias. This method is able to handle both aliases with and
+ * without that prefix. This does not lead to conflicts, because '$' is cannot
+ * be a valid part of any alias.
*
* Lookup of alias can fail if either alias does not exist in color alias
* source or color alias source itself does not exist
@@ -212,7 +228,8 @@ public final function Text ResolveWeapon(
* look up a value. Case-insensitive.
* @param copyOnFailure Whether method should return copy of original
* `alias` value in case caller source did not have any records
- * corresponding to `alias`.
+ * corresponding to `alias`. If `alias` was specified with '$' prefix -
+ * it will be discarded.
* @return If look up was successful - value, associated with the given
* alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
* flag: `copyOnFailure == false` means method will return `none`
@@ -223,17 +240,17 @@ public final function Text ResolveColor(
BaseText alias,
optional bool copyOnFailure)
{
- local AliasSource source;
- source = GetColorSource();
- if (source != none) {
- return source.Resolve(alias, copyOnFailure);
- }
- return none;
+ return ResolveWithSource(alias, colorAliasSource, copyOnFailure);
}
/**
- * Tries to look up a value stored for given alias in an `AliasSource`
- * configured to store feature aliases. Reports error on failure.
+ * Tries to look up a value stored for given alias in an `BaseAliasSource`
+ * configured to store feature aliases.
+ *
+ * In Acedia aliases are typically prefixed with '$' to indicate that user
+ * means to enter alias. This method is able to handle both aliases with and
+ * without that prefix. This does not lead to conflicts, because '$' is cannot
+ * be a valid part of any alias.
*
* Lookup of alias can fail if either alias does not exist in feature alias
* source or feature alias source itself does not exist
@@ -245,7 +262,8 @@ public final function Text ResolveColor(
* look up a value. Case-insensitive.
* @param copyOnFailure Whether method should return copy of original
* `alias` value in case caller source did not have any records
- * corresponding to `alias`.
+ * corresponding to `alias`. If `alias` was specified with '$' prefix -
+ * it will be discarded.
* @return If look up was successful - value, associated with the given
* alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
* flag: `copyOnFailure == false` means method will return `none`
@@ -256,17 +274,17 @@ public final function Text ResolveFeature(
BaseText alias,
optional bool copyOnFailure)
{
- local AliasSource source;
- source = GetFeatureSource();
- if (source != none) {
- return source.Resolve(alias, copyOnFailure);
- }
- return none;
+ return ResolveWithSource(alias, featureAliasSource, copyOnFailure);
}
/**
- * Tries to look up a value stored for given alias in an `AliasSource`
- * configured to store entity aliases. Reports error on failure.
+ * Tries to look up a value stored for given alias in an `BaseAliasSource`
+ * configured to store entity aliases.
+ *
+ * In Acedia aliases are typically prefixed with '$' to indicate that user
+ * means to enter alias. This method is able to handle both aliases with and
+ * without that prefix. This does not lead to conflicts, because '$' is cannot
+ * be a valid part of any alias.
*
* Lookup of alias can fail if either alias does not exist in entity alias
* source or entity alias source itself does not exist
@@ -278,7 +296,8 @@ public final function Text ResolveFeature(
* look up a value. Case-insensitive.
* @param copyOnFailure Whether method should return copy of original
* `alias` value in case caller source did not have any records
- * corresponding to `alias`.
+ * corresponding to `alias`. If `alias` was specified with '$' prefix -
+ * it will be discarded.
* @return If look up was successful - value, associated with the given
* alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
* flag: `copyOnFailure == false` means method will return `none`
@@ -289,23 +308,53 @@ public final function Text ResolveEntity(
BaseText alias,
optional bool copyOnFailure)
{
- local AliasSource source;
- source = GetEntitySource();
- if (source != none) {
- return source.Resolve(alias, copyOnFailure);
+ return ResolveWithSource(alias, entityAliasSource, copyOnFailure);
+}
+
+/**
+ * Tries to look up a value stored for given alias in a custom alias source
+ * with a given name `sourceName`.
+ *
+ * In Acedia aliases are typically prefixed with '$' to indicate that user
+ * means to enter alias. This method is able to handle both aliases with and
+ * without that prefix. This does not lead to conflicts, because '$' is cannot
+ * be a valid part of any alias.
+ *
+ * Custom alias sources are any type of alias source that isn't built-in into
+ * Acedia. They can either be added manually by the admin through config file.
+ *
+ * Lookup of alias can fail if either alias does not exist in entity alias
+ * source or entity alias source itself does not exist
+ * (due to either faulty configuration or incorrect definition).
+ * To determine if entity alias source exists you can check
+ * `_.alias.GetCustomSource()` value.
+ *
+ * @param alias Alias, for which method will attempt to
+ * look up a value. Case-insensitive.
+ * @param copyOnFailure Whether method should return copy of original
+ * `alias` value in case caller source did not have any records
+ * corresponding to `alias`. If `alias` was specified with '$' prefix -
+ * it will be discarded.
+ * @return If look up was successful - value, associated with the given
+ * alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
+ * flag: `copyOnFailure == false` means method will return `none`
+ * and `copyOnFailure == true` means method will return `alias.Copy()`.
+ * If `alias == none` method always returns `none`.
+ */
+public final function Text ResolveCustom(
+ BaseText sourceName,
+ BaseText alias,
+ optional bool copyOnFailure)
+{
+ local BaseAliasSource customSource;
+
+ customSource = GetCustomSource(sourceName);
+ if (customSource == none) {
+ return none;
}
- return none;
+ return ResolveWithSource(alias, customSource, copyOnFailure);
}
defaultproperties
{
- // TODO: all this shit below can be done as two messages
- noWeaponAliasSource = (l=LOG_Error,m="No weapon aliases source configured for Acedia's alias API. Error is most likely cause by erroneous config.")
- invalidWeaponAliasSource = (l=LOG_Error,m="`AliasSource` class `%1` is configured to store weapon aliases, but it seems to be invalid. This is a bug and not configuration file problem, but issue might be avoided by using a different `AliasSource`.")
- noColorAliasSource = (l=LOG_Error,m="No color aliases source configured for Acedia's alias API. Error is most likely cause by erroneous config.")
- invalidColorAliasSource = (l=LOG_Error,m="`AliasSource` class `%1` is configured to store color aliases, but it seems to be invalid. This is a bug and not configuration file problem, but issue might be avoided by using a different `AliasSource`.")
- noFeatureAliasSource = (l=LOG_Error,m="No feature aliases source configured for Acedia's alias API. Error is most likely cause by erroneous config.")
- invalidFeatureAliasSource = (l=LOG_Error,m="`AliasSource` class `%1` is configured to store feature aliases, but it seems to be invalid. This is a bug and not configuration file problem, but issue might be avoided by using a different `AliasSource`.")
- noEntityAliasSource = (l=LOG_Error,m="No entity aliases source configured for Acedia's alias API. Error is most likely cause by erroneous config.")
- invalidEntityAliasSource = (l=LOG_Error,m="`AliasSource` class `%1` is configured to store entity aliases, but it seems to be invalid. This is a bug and not configuration file problem, but issue might be avoided by using a different `AliasSource`.")
}
\ No newline at end of file
diff --git a/sources/Aliases/AliasesStorage.uc b/sources/Aliases/AliasesStorage.uc
new file mode 100644
index 0000000..791064f
--- /dev/null
+++ b/sources/Aliases/AliasesStorage.uc
@@ -0,0 +1,205 @@
+/**
+ * This is a simple helper object for `AliasSource` that can store
+ * an array of aliases in config files in a per-object-config manner.
+ * One `AliasesStorage` object can store several aliases for a single
+ * value.
+ * It is recommended that you do not try to access these objects directly.
+ * `AliasesStorage` is abstract, so it can never be created, for any
+ * actual use create a child class. Suggested name scheme is
+ * "Aliases", like "ColorAliases" or "WeaponAliases".
+ * It's only interesting function is storing '.'s as ':' in it's config,
+ * which is necessary to allow storing aliases for class names via
+ * these objects (since UnrealScript's cannot handle '.'s in object's names
+ * in it's configs).
+ * [INTERNAL] This is an internal object and should not be directly modified,
+ * its only allowed use is documented way to create new alias sources.
+ * Copyright 2019-2022 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 AliasesStorage extends AcediaObject
+ perObjectConfig
+ config(AcediaAliases)
+ abstract;
+
+// Aliases, recorded by this `AliasesStorage` object that all refer to
+// the same value, defined by this object's name `string(self.name)`
+var protected config array alias;
+
+// Name of the configurational file (without extension) where
+// this `AliasSource`'s data will be stored
+var protected const string configName;
+
+// Link to the `AliasSource` that uses `AliasesStorage` objects of this class.
+// To ensure that any `AliasesStorage` sub-class only belongs to one
+// `AliasSource`.
+var public const class sourceClass;
+
+// `true` means that this `AliasesStorage` object is awaiting saving into its
+// config
+var private bool pendingSaveToConfig;
+
+// Since:
+// 1. '.'s in values are converted into ':' for storage purposes;
+// 2. We have to store values in `string` to make use of config files.
+// we need methods to convert between "storage" (`string`)
+// and "actual" (`Text`) value version.
+// `ToStorageVersion()` and `ToActualVersion()` do that.
+private final static function string ToStorageVersion(BaseText actualValue)
+{
+ return Repl(actualValue.ToString(), ".", ":");
+}
+
+// See comment to `ToStorageVersion()`
+private final static function Text ToActualVersion(string storageValue)
+{
+ return __().text.FromString(Repl(storageValue, ":", "."));
+}
+
+/**
+ * Loads all `AliasesStorage` objects from their config file
+ * (defined in paired `AliasSource` class).
+ *
+ * This is an internal method and returned `AliasesStorage` objects require
+ * a special treatment: they aren't meant to be used with Acedia's reference
+ * counting - never release their references.
+ *
+ * @return Array of all `Aliases` objects, loaded from their config file.
+ */
+public static final function array LoadAllObjects()
+{
+ local int i;
+ local array objectNames;
+ local array loadedAliasObjects;
+
+ objectNames = GetPerObjectNames(default.configName,
+ string(default.class.name), MaxInt);
+ for (i = 0; i < objectNames.length; i += 1) {
+ loadedAliasObjects[i] = LoadObjectByName(objectNames[i]);
+ }
+ return loadedAliasObjects;
+}
+
+// Loads a new `AliasesStorage` object by it's given name (`objectName`).
+private static final function AliasesStorage LoadObjectByName(
+ string objectName)
+{
+ local AliasesStorage result;
+
+ // Since `MemoryAPI` for now does not support specifying names
+ // to created objects - do some manual dark magic and
+ // initialize this shit ourselves. Don't repeat this at home, kids.
+ result = new(none, objectName) default.class;
+ result._constructor();
+ return result;
+}
+
+/**
+ * Loads a new `AliasesStorage` object based on the value (`aliasesValue`)
+ * of it's aliases.
+ *
+ * @param aliasesValue Value that aliases in this `Aliases` object will
+ * correspond to.
+ * @return Instance of `AliasesStorage` object with a given name.
+ */
+public static final function AliasesStorage LoadObject(BaseText aliasesValue)
+{
+ if (aliasesValue != none) {
+ return LoadObjectByName(ToStorageVersion(aliasesValue));
+ }
+ return none;
+}
+
+/**
+ * Returns value that caller's `Aliases` object's aliases point to.
+ *
+ * @return Value, stored by this object.
+ */
+public final function Text GetValue()
+{
+ return ToActualVersion(string(self.name));
+}
+
+/**
+ * Returns array of aliases that caller `AliasesStorage` tells us point to
+ * it's value.
+ *
+ * @return Array of all aliases, stored by caller `AliasesStorage` object.
+ */
+public final function array GetAliases()
+{
+ local int i;
+ local array textAliases;
+
+ for (i = 0; i < alias.length; i += 1) {
+ textAliases[i] = _.text.FromString(alias[i]);
+ }
+ return textAliases;
+}
+
+/**
+ * [For inner use by `AliasSource`] Removes alias from this object.
+ *
+ * Does not update relevant `AliasHash`.
+ *
+ * Will prevent adding duplicate records inside it's own storage.
+ *
+ * @param aliasToRemove Alias to remove from caller `AliasesStorage` object.
+ * Expected to be in lower case.
+ */
+public final function RemoveAlias_S(string aliasToRemove)
+{
+ local int i;
+ local bool removedAlias;
+
+ while (i < alias.length)
+ {
+ if (aliasToRemove ~= alias[i])
+ {
+ alias.Remove(i, 1);
+ removedAlias = true;
+ }
+ else {
+ i += 1;
+ }
+ }
+ if (!pendingSaveToConfig)
+ {
+ pendingSaveToConfig = true;
+ _.scheduler.RequestDiskAccess(self).connect = SaveOrClear;
+ }
+}
+
+/**
+ * If this object still has any alias records, - forces a rewrite of it's data
+ * into the config file, otherwise - removes it's record entirely.
+ */
+private final function SaveOrClear()
+{
+ if (alias.length <= 0) {
+ ClearConfig();
+ }
+ else {
+ SaveConfig();
+ }
+ pendingSaveToConfig = false;
+}
+
+defaultproperties
+{
+ sourceClass = class'AliasSource'
+ configName = "AcediaAliases"
+}
\ No newline at end of file
diff --git a/sources/Aliases/Aliases_Feature.uc b/sources/Aliases/Aliases_Feature.uc
new file mode 100644
index 0000000..f1e8223
--- /dev/null
+++ b/sources/Aliases/Aliases_Feature.uc
@@ -0,0 +1,249 @@
+/**
+ * This feature provides a mechanism to define commands that automatically
+ * parse their arguments into standard Acedia collection. It also allows to
+ * manage them (and specify limitation on how they can be called) in a
+ * centralized manner.
+ * Copyright 2022 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 Aliases_Feature extends Feature
+ dependson(Aliases);
+
+struct ClassSourcePair
+{
+ var class class;
+ var AliasSource source;
+};
+// We don't want to reload `AliasSource`s several times when this feature is
+// disabled and re-enabled or its config is swapped, so we keep all loaded
+// sources here at all times and look them up from here
+var private array loadedSources;
+
+// Loaded `AliasSource`s
+var private AliasSource weaponAliasSource;
+var private AliasSource colorAliasSource;
+var private AliasSource featureAliasSource;
+var private AliasSource entityAliasSource;
+// Everything else
+var private HashTable customSources;
+
+var private LoggerAPI.Definition errCannotLoadAliasSource, errEmptyName;
+var private LoggerAPI.Definition errDuplicateCustomSource;
+
+protected function OnEnabled()
+{
+ _.alias._reloadSources();
+}
+
+protected function OnDisabled()
+{
+ DropSources();
+ _.alias._reloadSources();
+}
+
+private function DropSources()
+{
+ _.memory.Free(weaponAliasSource);
+ weaponAliasSource = none;
+ _.memory.Free(colorAliasSource);
+ colorAliasSource = none;
+ _.memory.Free(featureAliasSource);
+ featureAliasSource = none;
+ _.memory.Free(entityAliasSource);
+ entityAliasSource = none;
+ _.memory.Free(customSources);
+ customSources = none;
+}
+
+// Create each `AliasSource` instance only once to avoid any possible
+// nonsense with loading named objects several times: alias sources don't use
+// `AcediaConfig`s, so they don't automatically avoid named config reloading
+private function AliasSource GetSource(class sourceClass)
+{
+ local int i;
+ local AliasSource newSource;
+ local ClassSourcePair newPair;
+
+ if (sourceClass == none) {
+ return none;
+ }
+ for (i = 0; i < loadedSources.length; i += 1)
+ {
+ if (loadedSources[i].class == sourceClass) {
+ return loadedSources[i].source;
+ }
+ }
+ newSource = AliasSource(_.memory.Allocate(sourceClass));
+ if (newSource != none)
+ {
+ newPair.class = sourceClass;
+ newPair.source = newSource;
+ // One reference we store, one we return
+ newSource.NewRef();
+ }
+ else {
+ _.logger.Auto(errCannotLoadAliasSource).ArgClass(sourceClass);
+ }
+ return newSource;
+}
+
+protected function SwapConfig(FeatureConfig config)
+{
+ local Aliases newConfig;
+
+ newConfig = Aliases(config);
+ if (newConfig == none) {
+ return;
+ }
+ _.memory.Free(weaponAliasSource);
+ DropSources();
+ weaponAliasSource = GetSource(newConfig.weaponAliasSource);
+ colorAliasSource = GetSource(newConfig.colorAliasSource);
+ featureAliasSource = GetSource(newConfig.featureAliasSource);
+ entityAliasSource = GetSource(newConfig.entityAliasSource);
+ LoadCustomSources(newConfig.customSource);
+ _.alias._reloadSources();
+}
+
+private function LoadCustomSources(
+ array configCustomSources)
+{
+ local int i;
+ local bool reportedEmptyName;
+ local Text nextKey;
+ local AliasSource nextSource, conflictingSource;
+
+ _.memory.Free(customSources);
+ customSources = _.collections.EmptyHashTable();
+ for (i = 0; i < configCustomSources.length; i += 1)
+ {
+ if (configCustomSources[i].name == "" && !reportedEmptyName)
+ {
+ reportedEmptyName = true;
+ _.logger.Auto(errEmptyName);
+ }
+ nextKey = _.text.FromString(configCustomSources[i].name);
+ // We only store `AliasSource`s
+ conflictingSource = AliasSource(customSources.GetItem(nextKey));
+ if (conflictingSource != none)
+ {
+ _.logger.Auto(errDuplicateCustomSource)
+ .ArgClass(conflictingSource.class)
+ .Arg(nextKey) // Releases `nextKey`
+ .ArgClass(configCustomSources[i].source);
+ conflictingSource.FreeSelf();
+ continue;
+ }
+ nextSource = GetSource(configCustomSources[i].source);
+ if (nextSource != none)
+ {
+ customSources.SetItem(nextKey, nextSource);
+ nextSource.FreeSelf();
+ }
+ nextKey.FreeSelf();
+ }
+}
+
+/**
+ * Returns `AliasSource` for weapon aliases.
+ *
+ * @return `AliasSource`, configured to store weapon aliases.
+ */
+public function AliasSource GetWeaponSource()
+{
+ if (weaponAliasSource != none) {
+ weaponAliasSource.Newref();
+ }
+ return weaponAliasSource;
+}
+
+/**
+ * Returns `AliasSource` for color aliases.
+ *
+ * @return `AliasSource`, configured to store color aliases.
+ */
+public function AliasSource GetColorSource()
+{
+ if (colorAliasSource != none) {
+ colorAliasSource.Newref();
+ }
+ return colorAliasSource;
+}
+
+/**
+ * Returns `AliasSource` for feature aliases.
+ *
+ * @return `AliasSource`, configured to store feature aliases.
+ */
+public function AliasSource GetFeatureSource()
+{
+ if (featureAliasSource != none) {
+ featureAliasSource.Newref();
+ }
+ return featureAliasSource;
+}
+
+/**
+ * Returns `AliasSource` for entity aliases.
+ *
+ * @return `AliasSource`, configured to store entity aliases.
+ */
+public function AliasSource GetEntitySource()
+{
+ if (entityAliasSource != none) {
+ entityAliasSource.Newref();
+ }
+ return entityAliasSource;
+}
+
+/**
+ * Returns custom `AliasSource` with a given name.
+ *
+ * @return Custom `AliasSource`, configured with a given name `sourceName`.
+ */
+public function AliasSource GetCustomSource(BaseText sourceName)
+{
+ if (sourceName == none) {
+ return none;
+ }
+ // We only store `AliasSource`s
+ return AliasSource(customSources.GetItem(sourceName));
+}
+
+/**
+ * Returns custom `AliasSource` with a given name `sourceName.
+ *
+ * @return Custom `AliasSource`, configured with a given name `sourceName`.
+ */
+public function AliasSource GetCustomSource_S(string sourceName)
+{
+ local Text wrapper;
+ local AliasSource result;
+
+ wrapper = _.text.FromString(sourceName);
+ result = GetCustomSource(wrapper);
+ wrapper.FreeSelf();
+ return result;
+}
+
+defaultproperties
+{
+ configClass = class'Aliases'
+ errEmptyName = (l=LOG_Error,m="Empty name provided for the custom alias source. This is likely due to an erroneous config.")
+ errCannotLoadAliasSource = (l=LOG_Error,m="Failed to load alias source class `%1`.")
+ errDuplicateCustomSource = (l=LOG_Error,m="Custom alias source `%1` is already registered with name '%2'. Alias source `%3` with the same name will be ignored.")
+}
\ No newline at end of file
diff --git a/sources/Aliases/BaseAliasSource.uc b/sources/Aliases/BaseAliasSource.uc
new file mode 100644
index 0000000..0244ce2
--- /dev/null
+++ b/sources/Aliases/BaseAliasSource.uc
@@ -0,0 +1,283 @@
+/**
+ * Aliases allow users to define human-readable and easier to use
+ * "synonyms" to some symbol sequences (mainly names of UnrealScript classes).
+ * This is an interface class that can be implemented in various different
+ * ways.
+ * Several `AliasSource`s are supposed to exist separately, each storing
+ * aliases of particular kind: for weapon, zeds, colors, etc..
+ * Copyright 2022 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 BaseAliasSource extends AcediaObject
+ abstract;
+
+/**
+ * # `AliasSource`
+ *
+ * Interface for any alias source instance that includes basic methods for
+ * alias lookup and adding/removal.
+ *
+ * ## Aliases
+ *
+ * Aliases in Acedia are usually defined as either `$`.
+ * `` can only contain ASCII latin letters and digits.
+ * `` is meant to define a human-readable name for something
+ * (e.g. "m14" for `KFMod.M14EBRBattleRifle`).
+ * Aliases are *case-insensitive*.
+ * '$' prefix is used to emphasize that what user is specifying is,
+ * in fact, an alias and it *is not* actually important for aliases feature
+ * and API: only `` is used. However, for convenience's sake,
+ * `AliasesAPI` usually recognizes aliases both with and without '$' prefix.
+ * Alias sources (classes derived from `BaseAliasSource`), however should only
+ * handle resolving ``, treating '$' prefix as a mistake in alias
+ * parameter.
+ *
+ * ## Implementation
+ *
+ * As far as implementation goes, it's up to you how your own child alias
+ * source class is configured to obtain its aliases, but `Aliases_Feature`
+ * should only create a single instance for every source class (although
+ * nothing can prevent other mods from creating more instances, so we cannot
+ * give any guarantees).
+ * Methods that add or remove aliases are allowed to fail for whatever
+ * reason is valid for your source's case (it might forbid adding aliases
+ * at all), as long as they return `false`.
+ * Although all built-in aliases are storing case-insensitive values,
+ * `BaseAliasSource` does not demand that and allows to configure this behavior
+ * via `AreValuesCaseSensitive()`. This is important for `GetAliases()` method
+ * that returns all aliases referring to a given value.
+ */
+
+/**
+ * Returns whether caller alias source class stores case-sensitive values.
+ *
+ * This information is necessary for organizing lookup of aliases by
+ * a given value. Aliases themselves are always case-insensitive.
+ *
+ * This method should not change returned value for any fixed class.
+ *
+ * @return `true` if stored values are case-sensitive and `false` if they are
+ * case-insensitive.
+ */
+public static function bool AreValuesCaseSensitive();
+
+/**
+ * Returns all aliases that represent given value `value`.
+ *
+ * @param value Value for which to return all its aliases.
+ * Whether it is treated as case-sensitive is decided by
+ * `AreValuesCaseSensitive()` method, but all default alias sources are
+ * case-insensitive.
+ * @return Array of all aliases that refer to the given `value` inside
+ * the caller alias source. All `Text` references are guaranteed to not be
+ * `none` or duplicated.
+ */
+public function array GetAliases(BaseText value);
+
+/**
+ * Returns all aliases that represent given value `value`.
+ *
+ * @param value Value for which to return all its aliases.
+ * Whether it is treated as case-sensitive is decided by
+ * `AreValuesCaseSensitive()` method, but all default alias sources are
+ * case-insensitive.
+ * @return Array of all aliases that refer to the given `value` inside
+ * the caller alias source. All `string` references are guaranteed to not
+ * be duplicated.
+ */
+public function array GetAliases_S(string value)
+{
+ local int i;
+ local Text valueAsText;
+ local array resultWithTexts;
+ local array result;
+
+ valueAsText = _.text.FromString(value);
+ resultWithTexts = GetAliases(valueAsText);
+ _.memory.Free(valueAsText);
+ for (i = 0; i < resultWithTexts.length; i += 1) {
+ result[result.length] = resultWithTexts[i].ToString();
+ }
+ _.memory.FreeMany(resultWithTexts);
+ return result;
+}
+
+/**
+ * Checks if given alias is present in caller `AliasSource`.
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param alias Alias to check, case-insensitive.
+ * @return `true` if present, `false` otherwise.
+ */
+public function bool HasAlias(BaseText alias);
+
+/**
+ * Checks if given alias is present in caller `AliasSource`.
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param alias Alias to check, case-insensitive.
+ * @return `true` if present, `false` otherwise.
+ */
+public function bool HasAlias_S(string alias)
+{
+ local bool result;
+ local Text aliasAsText;
+
+ aliasAsText = _.text.FromString(alias);
+ result = HasAlias(aliasAsText);
+ _.memory.Free(aliasAsText);
+ return result;
+}
+
+/**
+ * Returns value stored for the given alias in caller `AliasSource`
+ * (as well as it's `Aliases` objects).
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param alias Alias, for which method will attempt to return
+ * a value. Case-insensitive. If given `alias` starts with "$" character -
+ * that character will be removed before resolving that alias.
+ * @param copyOnFailure Whether method should return copy of original
+ * `alias` value in case caller source did not have any records
+ * corresponding to `alias`.
+ * @return If look up was successful - value, associated with the given
+ * alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
+ * flag: `copyOnFailure == false` means method will return `none`
+ * and `copyOnFailure == true` means method will return `alias.Copy()`.
+ * If `alias == none` method always returns `none`.
+ */
+public function Text Resolve(BaseText alias, optional bool copyOnFailure);
+
+/**
+ * Returns value stored for the given alias in caller `AliasSource`
+ * (as well as it's `Aliases` objects).
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param alias Alias, for which method will attempt to return
+ * a value. Case-insensitive. If given `alias` starts with "$" character -
+ * that character will be removed before resolving that alias.
+ * @param copyOnFailure Whether method should return copy of original
+ * `alias` value in case caller source did not have any records
+ * corresponding to `alias`.
+ * @return If look up was successful - value, associated with the given
+ * alias `alias`. If lookup was unsuccessful, it depends on `copyOnFailure`
+ * flag: `copyOnFailure == false` means method will return empty `string`
+ * and `copyOnFailure == true` means method will return `alias`.
+ */
+public function string Resolve_S(
+ string alias,
+ optional bool copyOnFailure)
+{
+ local Text resultAsText;
+ local Text aliasAsText;
+
+ aliasAsText = _.text.FromString(alias);
+ resultAsText = Resolve(aliasAsText, copyOnFailure);
+ return _.text.IntoString(resultAsText);
+}
+
+/**
+ * Adds another alias to the caller `AliasSource`.
+ * If alias with the same name as `aliasToAdd` already exists - method
+ * overwrites it.
+ *
+ * Can fail iff `aliasToAdd` is an invalid alias or `aliasValue == none`.
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param aliasToAdd Alias that you want to add to caller source.
+ * Alias names are case-insensitive.
+ * @param aliasValue Intended value of this alias.
+ * @return `true` if alias was added and `false` otherwise (alias was invalid).
+ */
+public function bool AddAlias(BaseText aliasToAdd, BaseText aliasValue);
+
+/**
+ * Adds another alias to the caller `AliasSource`.
+ * If alias with the same name as `aliasToAdd` already exists, -
+ * method overwrites it.
+ *
+ * Can fail iff `aliasToAdd` is an invalid alias.
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param aliasToAdd Alias that you want to add to caller source.
+ * Alias names are case-insensitive.
+ * @param aliasValue Intended value of this alias.
+ * @return `true` if alias was added and `false` otherwise (alias was invalid).
+ */
+public function bool AddAlias_S(string aliasToAdd, string aliasValue)
+{
+ local bool result;
+ local Text aliasAsText, valueAsText;
+
+ aliasAsText = _.text.FromString(aliasToAdd);
+ valueAsText = _.text.FromString(aliasValue);
+ result = AddAlias(aliasAsText, valueAsText);
+ _.memory.Free(aliasAsText);
+ _.memory.Free(valueAsText);
+ return result;
+}
+
+/**
+ * Removes alias (all records with it, in case of duplicates) from
+ * the caller `AliasSource`.
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param aliasToRemove Alias that you want to remove from caller source.
+ * @return `true` if an alias was present in the source and was deleted and
+ * `false` if there was no specified alias in the first place.
+ */
+public function bool RemoveAlias(BaseText aliasToRemove);
+
+/**
+ * Removes alias (all records with it, in case of duplicates) from
+ * the caller `AliasSource`.
+ *
+ * NOTE: having '$' prefix is considered to be invalid for `alias` by this
+ * method.
+ *
+ * @param aliasToRemove Alias that you want to remove from caller source.
+ * @return `true` if an alias was present in the source and was deleted and
+ * `false` if there was no specified alias in the first place.
+ */
+public function bool RemoveAlias_S(string aliasToRemove)
+{
+ local bool result;
+ local Text aliasAsText;
+
+ aliasAsText = _.text.FromString(aliasToRemove);
+ result = RemoveAlias(aliasAsText);
+ _.memory.Free(aliasAsText);
+ return result;
+}
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Aliases/BuiltInSources/ColorAliases.uc b/sources/Aliases/BuiltInSources/ColorAliases.uc
index 9a00b78..391e6a5 100644
--- a/sources/Aliases/BuiltInSources/ColorAliases.uc
+++ b/sources/Aliases/BuiltInSources/ColorAliases.uc
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class ColorAliases extends Aliases
+class ColorAliases extends AliasesStorage
perObjectConfig
config(AcediaAliases_Colors);
diff --git a/sources/Aliases/BuiltInSources/EntityAliases.uc b/sources/Aliases/BuiltInSources/EntityAliases.uc
index 3b41559..69be1f8 100644
--- a/sources/Aliases/BuiltInSources/EntityAliases.uc
+++ b/sources/Aliases/BuiltInSources/EntityAliases.uc
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class EntityAliases extends Aliases
+class EntityAliases extends AliasesStorage
perObjectConfig
config(AcediaAliases_Entities);
diff --git a/sources/Aliases/BuiltInSources/FeatureAliasSource.uc b/sources/Aliases/BuiltInSources/FeatureAliasSource.uc
index 9167b6d..e58bfe8 100644
--- a/sources/Aliases/BuiltInSources/FeatureAliasSource.uc
+++ b/sources/Aliases/BuiltInSources/FeatureAliasSource.uc
@@ -18,7 +18,7 @@
* along with Acedia. If not, see .
*/
class FeatureAliasSource extends AliasSource
- config(AcediaAliases);
+ config(AcediaAliases_Features);
defaultproperties
{
diff --git a/sources/Aliases/BuiltInSources/FeatureAliases.uc b/sources/Aliases/BuiltInSources/FeatureAliases.uc
index 7acc0b6..2cfed3b 100644
--- a/sources/Aliases/BuiltInSources/FeatureAliases.uc
+++ b/sources/Aliases/BuiltInSources/FeatureAliases.uc
@@ -17,9 +17,9 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class FeatureAliases extends Aliases
+class FeatureAliases extends AliasesStorage
perObjectConfig
- config(AcediaAliases);
+ config(AcediaAliases_Features);
defaultproperties
{
diff --git a/sources/Aliases/BuiltInSources/WeaponAliases.uc b/sources/Aliases/BuiltInSources/WeaponAliases.uc
index 03f84d3..85d81ed 100644
--- a/sources/Aliases/BuiltInSources/WeaponAliases.uc
+++ b/sources/Aliases/BuiltInSources/WeaponAliases.uc
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class WeaponAliases extends Aliases
+class WeaponAliases extends AliasesStorage
perObjectConfig
config(AcediaAliases_Weapons);
diff --git a/sources/Aliases/Tests/MockAliases.uc b/sources/Aliases/Tests/MockAliases.uc
index dcc73e6..ef9d71c 100644
--- a/sources/Aliases/Tests/MockAliases.uc
+++ b/sources/Aliases/Tests/MockAliases.uc
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class MockAliases extends Aliases
+class MockAliases extends AliasesStorage
perObjectConfig
config(AcediaAliases_Tests);
diff --git a/sources/Aliases/Tests/TEST_Aliases.uc b/sources/Aliases/Tests/TEST_Aliases.uc
index 82700a0..213b5ef 100644
--- a/sources/Aliases/Tests/TEST_Aliases.uc
+++ b/sources/Aliases/Tests/TEST_Aliases.uc
@@ -23,15 +23,17 @@ class TEST_Aliases extends TestCase
protected static function TESTS()
{
Context("Testing loading aliases from a mock object `MockAliasSource`.");
+ Issue("`GetCustomSource()` fails to return alias source.");
+ TEST_ExpectNotNone(__().alias.GetCustomSource(P("mock")));
SubTest_AliasLoadingCorrect();
SubTest_AliasLoadingIncorrect();
}
protected static function SubTest_AliasLoadingCorrect()
{
- local AliasSource source;
+ local BaseAliasSource source;
Issue("`Resolve()` fails to return alias value that should be loaded.");
- source = __().alias.GetCustomSource(class'MockAliasSource');
+ source = __().alias.GetCustomSource(P("mock"));
TEST_ExpectTrue(source.Resolve(P("Global")).ToString() == "value");
TEST_ExpectTrue(source.Resolve(P("ford")).ToString() == "car");
@@ -51,21 +53,19 @@ protected static function SubTest_AliasLoadingCorrect()
TEST_ExpectTrue( source.Resolve(P("HardToBeAGod")).ToString()
== "sci.fi");
- Issue("Aliases with empty values in alias name or their value are handled"
- @ "incorrectly.");
- TEST_ExpectTrue(source.Resolve(P("")).ToString() == "empty");
+ Issue("Aliases corresponding to empty values are handled incorrectly.");
TEST_ExpectTrue(source.Resolve(P("also")).ToString() == "");
}
protected static function SubTest_AliasLoadingIncorrect()
{
- local AliasSource source;
+ local BaseAliasSource source;
Issue("`AliasAPI` cannot return value custom source.");
- source = __().alias.GetCustomSource(class'MockAliasSource');
+ source = __().alias.GetCustomSource(P("mock"));
TEST_ExpectNotNone(source);
Issue("`Resolve()` reports success of finding inexistent alias.");
- source = __().alias.GetCustomSource(class'MockAliasSource');
+ source = __().alias.GetCustomSource(P("mock"));
TEST_ExpectNone(source.Resolve(P("noSuchThing")));
Issue("`HasAlias()` reports inexistent alias as present.");
diff --git a/sources/BaseRealm/AcediaEnvironment/AcediaEnvironment.uc b/sources/BaseRealm/AcediaEnvironment/AcediaEnvironment.uc
index 4a0269b..0eaf13c 100644
--- a/sources/BaseRealm/AcediaEnvironment/AcediaEnvironment.uc
+++ b/sources/BaseRealm/AcediaEnvironment/AcediaEnvironment.uc
@@ -23,7 +23,7 @@ class AcediaEnvironment extends AcediaObject;
* # `AcediaEnvironment`
*
* Instance of this class will be used by Acedia to manage resources available
- * from different packages like `Feature`s, aliases, etc..
+ * from different packages like `Feature`s and such other etc..
* This is mostly necessary to implement Acedia loader (and, possibly,
* its alternatives) that would load available packages and enable `Feature`s
* admin wants to be enabled.
@@ -244,13 +244,6 @@ private final function ReadManifest(class<_manifest> manifestClass)
{
local int i;
- for (i = 0; i < manifestClass.default.aliasSources.length; i += 1)
- {
- if (manifestClass.default.aliasSources[i] == none) {
- continue;
- }
- _.memory.Allocate(manifestClass.default.aliasSources[i]);
- }
for (i = 0; i < manifestClass.default.features.length; i += 1)
{
if (manifestClass.default.features[i] == none) {
diff --git a/sources/Commands/Aliases/CommandAliasSource.uc b/sources/Commands/Aliases/CommandAliasSource.uc
index 745452f..c37c57f 100644
--- a/sources/Commands/Aliases/CommandAliasSource.uc
+++ b/sources/Commands/Aliases/CommandAliasSource.uc
@@ -17,7 +17,8 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class CommandAliasSource extends AliasSource;
+class CommandAliasSource extends AliasSource
+ config(AcediaAliases_Commands);
defaultproperties
{
diff --git a/sources/Commands/Aliases/CommandAliases.uc b/sources/Commands/Aliases/CommandAliases.uc
index 3faaf93..4ef02ba 100644
--- a/sources/Commands/Aliases/CommandAliases.uc
+++ b/sources/Commands/Aliases/CommandAliases.uc
@@ -17,8 +17,9 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class CommandAliases extends Aliases
- perObjectConfig;
+class CommandAliases extends AliasesStorage
+ perObjectConfig
+ config(AcediaAliases_Commands);
defaultproperties
{
diff --git a/sources/Manifest.uc b/sources/Manifest.uc
index 9c43224..581c733 100644
--- a/sources/Manifest.uc
+++ b/sources/Manifest.uc
@@ -22,13 +22,9 @@
defaultproperties
{
- features(0) = class'Commands_Feature'
- features(1) = class'Avarice_Feature'
- aliasSources(0) = class'AliasSource'
- aliasSources(1) = class'WeaponAliasSource'
- aliasSources(2) = class'ColorAliasSource'
- aliasSources(3) = class'FeatureAliasSource'
- aliasSources(4) = class'EntityAliasSource'
+ features(0) = class'Aliases_Feature'
+ features(1) = class'Commands_Feature'
+ features(2) = class'Avarice_Feature'
testCases(0) = class'TEST_Base'
testCases(1) = class'TEST_ActorService'
testCases(2) = class'TEST_Boxes'