Refactor Aliases subsystem
This commit is contained in:
parent
3849fd5c9d
commit
27c88b8707
218
sources/Core/Aliases/AliasHash.uc
Normal file
218
sources/Core/Aliases/AliasHash.uc
Normal file
@ -0,0 +1,218 @@
|
||||
/**
|
||||
* A class, implementing a hash-table-based dictionary for quick access to
|
||||
* aliases' values.
|
||||
* It does not support dynamic hash table capacity change and
|
||||
* requires to set the size upfront.
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class AliasHash extends AcediaObject
|
||||
dependson(AliasSource)
|
||||
config(AcediaSystem);
|
||||
|
||||
// Reasonable lower and upper limits on hash table capacity,
|
||||
// that will be enforced if user requires something outside those bounds
|
||||
var private config const int MINIMUM_CAPACITY;
|
||||
var private config const int MAXIMUM_CAPACITY;
|
||||
|
||||
// Bucket of alias-value pairs, with the same alias hash.
|
||||
struct PairBucket
|
||||
{
|
||||
var array<AliasSource.AliasValuePair> pairs;
|
||||
};
|
||||
var private array<PairBucket> hashTable;
|
||||
|
||||
/**
|
||||
* Initializes caller `AliasHash`.
|
||||
*
|
||||
* Calling this function again will clear all existing data and will create
|
||||
* a brand new hash table.
|
||||
*
|
||||
* @param desiredCapacity Desired capacity of the underlying hash table.
|
||||
* Will be clamped between `MINIMUM_CAPACITY` and `MAXIMUM_CAPACITY`.
|
||||
* Not specifying anything as this parameter creates a hash table of
|
||||
* size `MINIMUM_CAPACITY`.
|
||||
* @return A reference to a caller object to allow for function chaining.
|
||||
*/
|
||||
public final function AliasHash Initialize(optional int desiredCapacity)
|
||||
{
|
||||
desiredCapacity = Clamp(desiredCapacity, MINIMUM_CAPACITY,
|
||||
MAXIMUM_CAPACITY);
|
||||
hashTable.length = 0;
|
||||
hashTable.length = desiredCapacity;
|
||||
return self;
|
||||
}
|
||||
|
||||
// Helper method that is needed as a replacement for `%`, since it is
|
||||
// an operation on `float`s in UnrealScript and does not have enough precision
|
||||
// to work with hashes.
|
||||
// Assumes positive input.
|
||||
private function int Remainder(int number, int divisor)
|
||||
{
|
||||
local int quotient;
|
||||
quotient = number / divisor;
|
||||
return (number - quotient * divisor);
|
||||
}
|
||||
|
||||
// Finds indices for:
|
||||
// 1. Bucked that contains specified alias (`bucketIndex`);
|
||||
// 2. Pair for specified alias in the bucket's collection (`pairIndex`).
|
||||
// `bucketIndex` is always found,
|
||||
// `pairIndex` is valid iff method returns `true`.
|
||||
private final function bool FindPairIndices(
|
||||
string alias,
|
||||
out int bucketIndex,
|
||||
out int pairIndex)
|
||||
{
|
||||
local int i;
|
||||
local array<AliasSource.AliasValuePair> bucketPairs;
|
||||
// `Locs()` is used because aliases are case-insensitive.
|
||||
bucketIndex = _().text.GetHash(Locs(alias));
|
||||
if (bucketIndex < 0) {
|
||||
bucketIndex *= -1;
|
||||
}
|
||||
bucketIndex = Remainder(bucketIndex, hashTable.length);
|
||||
// Check if bucket actually has given alias.
|
||||
bucketPairs = hashTable[bucketIndex].pairs;
|
||||
for (i = 0; i < bucketPairs.length; i += 1)
|
||||
{
|
||||
if (bucketPairs[i].alias ~= alias)
|
||||
{
|
||||
pairIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a value for a given alias.
|
||||
*
|
||||
* @param alias Alias for which we need to find a value.
|
||||
* Aliases are case-insensitive.
|
||||
* @param value If given alias is present in caller `AliasHash`, -
|
||||
* it's value will be written in this variable.
|
||||
* Otherwise value is undefined.
|
||||
* @return `true` if we found value, `false` otherwise.
|
||||
*/
|
||||
public final function bool Find(string alias, out string value)
|
||||
{
|
||||
local int bucketIndex;
|
||||
local int pairIndex;
|
||||
if (FindPairIndices(alias, bucketIndex, pairIndex))
|
||||
{
|
||||
value = hashTable[bucketIndex].pairs[pairIndex].value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if caller `AliasHash` contains given alias.
|
||||
*
|
||||
* @param alias Alias to check for belonging to caller `AliasHash`.
|
||||
* Aliases are case-insensitive.
|
||||
* @return `true` if caller `AliasHash` contains the value for a given alias
|
||||
* and `false` otherwise.
|
||||
*/
|
||||
public final function bool Contains(string alias)
|
||||
{
|
||||
local int bucketIndex;
|
||||
local int pairIndex;
|
||||
return FindPairIndices(alias, bucketIndex, pairIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new record for alias `alias` for value of `value`.
|
||||
*
|
||||
* If there is already a value for a given `alias` - it will be overwritten.
|
||||
*
|
||||
* @param alias Alias to insert. Aliases are case-insensitive.
|
||||
* @param value Value for a given alias to store.
|
||||
* @return A reference to a caller object to allow for function chaining.
|
||||
*/
|
||||
public final function AliasHash Insert(string alias, string value)
|
||||
{
|
||||
local int bucketIndex;
|
||||
local int pairIndex;
|
||||
local AliasSource.AliasValuePair newRecord;
|
||||
newRecord.value = value;
|
||||
newRecord.alias = alias;
|
||||
if (!FindPairIndices(alias, bucketIndex, pairIndex)) {
|
||||
pairIndex = hashTable[bucketIndex].pairs.length;
|
||||
}
|
||||
hashTable[bucketIndex].pairs[pairIndex] = newRecord;
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new record for alias `alias` for value of `value`.
|
||||
*
|
||||
* If there is already a value for a given `alias`, - new value will be
|
||||
* discarded and `AliasHash` will not be changed.
|
||||
*
|
||||
* @param alias Alias to insert. Aliases are case-insensitive.
|
||||
* @param value Value for a given alias to store.
|
||||
* @param existingValue Value that will correspond to a given alias after
|
||||
* this method's execution. If insertion was successful - given `value`,
|
||||
* otherwise (if there already was a record for an `alias`)
|
||||
* it will return value that already existed in caller `AliasHash`.
|
||||
* @return `true` if given alias-value pair was inserted and `false` otherwise.
|
||||
*/
|
||||
public final function bool InsertIfMissing(
|
||||
string alias,
|
||||
string value,
|
||||
out string existingValue)
|
||||
{
|
||||
local int bucketIndex;
|
||||
local int pairIndex;
|
||||
local AliasSource.AliasValuePair newRecord;
|
||||
newRecord.value = value;
|
||||
newRecord.alias = alias;
|
||||
existingValue = value;
|
||||
if (FindPairIndices(alias, bucketIndex, pairIndex)) {
|
||||
existingValue = hashTable[bucketIndex].pairs[pairIndex].value;
|
||||
return false;
|
||||
}
|
||||
pairIndex = hashTable[bucketIndex].pairs.length;
|
||||
hashTable[bucketIndex].pairs[pairIndex] = newRecord;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes record, corresponding to a given alias `alias`.
|
||||
*
|
||||
* @param alias Alias for which all records must be removed.
|
||||
* @return `true` if record was removed, `false` if id did not
|
||||
* (can only happen when `AliasHash` did not have any records for `alias`).
|
||||
*/
|
||||
public final function bool Remove(string alias)
|
||||
{
|
||||
local int bucketIndex;
|
||||
local int pairIndex;
|
||||
if (FindPairIndices(alias, bucketIndex, pairIndex)) {
|
||||
hashTable[bucketIndex].pairs.Remove(pairIndex, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
MINIMUM_CAPACITY = 10
|
||||
MAXIMUM_CAPACITY = 100000
|
||||
}
|
135
sources/Core/Aliases/AliasService.uc
Normal file
135
sources/Core/Aliases/AliasService.uc
Normal file
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class AliasService extends Service
|
||||
config(AcediaSystem);
|
||||
|
||||
// Objects for which we are yet to write configs
|
||||
var private array<AliasSource> sourcesPendingToSave;
|
||||
var private array<Aliases> 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<AliasSource> weaponAliasesSource;
|
||||
var public config const class<AliasSource> colorAliasesSource;
|
||||
|
||||
protected function OnLaunch()
|
||||
{
|
||||
local float actualInterval;
|
||||
actualInterval = saveInterval;
|
||||
if (actualInterval <= 0)
|
||||
{
|
||||
actualInterval = 0.05;
|
||||
}
|
||||
SetTimer(actualInterval, true);
|
||||
}
|
||||
|
||||
protected function OnShutdown()
|
||||
{
|
||||
SaveAllPendingObjects();
|
||||
}
|
||||
|
||||
public 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 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 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 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;
|
||||
}
|
||||
|
||||
event Timer()
|
||||
{
|
||||
DoSaveNextPendingObject();
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
saveInterval = 0.05
|
||||
weaponAliasesSource = class'WeaponAliasSource'
|
||||
colorAliasesSource = class'ColorAliasSource'
|
||||
}
|
379
sources/Core/Aliases/AliasSource.uc
Normal file
379
sources/Core/Aliases/AliasSource.uc
Normal file
@ -0,0 +1,379 @@
|
||||
/**
|
||||
* 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 Anton Tarasenko
|
||||
*------------------------------------------------------------------------------
|
||||
* This file is part of Acedia.
|
||||
*
|
||||
* Acedia is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Acedia is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class AliasSource extends Singleton
|
||||
config(AcediaAliases);
|
||||
|
||||
// Name of the configurational file (without extension) where
|
||||
// this `AliasSource`'s data will be stored.
|
||||
var private const string configName;
|
||||
|
||||
// (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<Aliases> 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<Aliases> loadedAliasObjects;
|
||||
|
||||
// Links alias to a value.
|
||||
// An array of these structures (without duplicate `alias` records) defines
|
||||
// a function from the space of aliases to the space of values.
|
||||
struct AliasValuePair
|
||||
{
|
||||
var string alias;
|
||||
var string value;
|
||||
};
|
||||
// Aliases data for saving and loading on a disk (ini-file).
|
||||
// Name is chosen to make configurational files more readable.
|
||||
var private config array<AliasValuePair> record;
|
||||
// Hash table for a 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 AliasHash hash;
|
||||
|
||||
|
||||
// How many times bigger capacity of `hash` should be, compared to amount of
|
||||
// initially loaded data from a config.
|
||||
var private const float HASH_TABLE_SCALE;
|
||||
|
||||
// Load and hash all the data `AliasSource` creation.
|
||||
protected function OnCreated()
|
||||
{
|
||||
local int entriesAmount;
|
||||
if (!AssertAliasesClassIsOwnedByMe()) {
|
||||
return;
|
||||
}
|
||||
// Load and hash
|
||||
entriesAmount = LoadData();
|
||||
hash = AliasHash(_.memory.Allocate(class'AliasHash'));
|
||||
hash.Initialize(int(entriesAmount * HASH_TABLE_SCALE));
|
||||
HashValidAliases();
|
||||
}
|
||||
|
||||
// Ensures invariant of our `Aliases` class only belonging to us by
|
||||
// itself ourselves otherwise.
|
||||
private final function bool AssertAliasesClassIsOwnedByMe()
|
||||
{
|
||||
if (aliasesClass == none) return true;
|
||||
if (aliasesClass.default.sourceClass == class) return true;
|
||||
_.logger.Failure("`AliasSource`-`Aliases` class pair is incorrectly"
|
||||
@ "setup for source `" $ string(class) $ "`. Omitting it.");
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
// This method loads all the defined aliases from the config file and
|
||||
// returns how many entries are there are total.
|
||||
// Does not change data, including fixing duplicates.
|
||||
private final function int LoadData()
|
||||
{
|
||||
local int i;
|
||||
local int entriesAmount;
|
||||
local array<string> objectNames;
|
||||
entriesAmount = record.length;
|
||||
if (aliasesClass == none) {
|
||||
return entriesAmount;
|
||||
}
|
||||
objectNames =
|
||||
GetPerObjectNames(configName, string(aliasesClass.name), MaxInt);
|
||||
loadedAliasObjects.length = objectNames.length;
|
||||
for (i = 0; i < objectNames.length; i += 1)
|
||||
{
|
||||
loadedAliasObjects[i] = new(none, objectNames[i]) aliasesClass;
|
||||
entriesAmount += loadedAliasObjects[i].GetAliases().length;
|
||||
}
|
||||
return entriesAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply checks if given alias is present in caller `AliasSource`.
|
||||
*
|
||||
* @param alias Alias to check, case-insensitive.
|
||||
* @return `true` if present, `false` otherwise.
|
||||
*/
|
||||
public function bool ContainsAlias(string alias)
|
||||
{
|
||||
return hash.Contains(alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to look up a value, stored for given alias in caller `AliasSource` and
|
||||
* reports error upon failure.
|
||||
*
|
||||
* Also see `Try()` method.
|
||||
*
|
||||
* @param alias Alias, for which method will attempt to look up a value.
|
||||
* Case-insensitive.
|
||||
* @param value If passed `alias` was recorded in caller `AliasSource`,
|
||||
* it's corresponding value will be written in this variable.
|
||||
* Otherwise value is undefined.
|
||||
* @return `true` if lookup was successful (alias present in 'AliasSource`)
|
||||
* and correct value was written into `value`, `false` otherwise.
|
||||
*/
|
||||
public function bool Resolve(string alias, out string value)
|
||||
{
|
||||
return hash.Find(alias, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to look up a value, stored for given alias in caller `AliasSource` and
|
||||
* silently returns given `alias` value upon failure.
|
||||
*
|
||||
* Also see `Resolve()` method.
|
||||
*
|
||||
* @param alias Alias, for which method will attempt to look up a value.
|
||||
* Case-insensitive.
|
||||
* @return Value corresponding to a given alias, if it was present in
|
||||
* caller `AliasSource` and value of `alias` parameter instead.
|
||||
*/
|
||||
public function string Try(string alias)
|
||||
{
|
||||
local string result;
|
||||
if (hash.Find(alias, result)) {
|
||||
return result;
|
||||
}
|
||||
return alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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 final function bool AddAlias(
|
||||
string aliasToAdd,
|
||||
string aliasValue,
|
||||
optional bool saveInObject)
|
||||
{
|
||||
local AliasValuePair newPair;
|
||||
if (_.alias.IsAliasValid(aliasToAdd)) {
|
||||
return false;
|
||||
}
|
||||
if (hash.Contains(aliasToAdd)) {
|
||||
RemoveAlias(aliasToAdd);
|
||||
}
|
||||
// We might not be able to use per-object-config storage
|
||||
if (saveInObject && aliasesClass == none) {
|
||||
saveInObject = false;
|
||||
_.logger.Warning("Cannot save alias in object for source `"
|
||||
$ string(class)
|
||||
$ "`, because it does not have appropriate `Aliases` class setup.");
|
||||
}
|
||||
// Save
|
||||
if (saveInObject) {
|
||||
GetAliasesObjectWithValue(aliasValue).AddAlias(aliasToAdd);
|
||||
}
|
||||
else
|
||||
{
|
||||
newPair.alias = aliasToAdd;
|
||||
newPair.value = aliasValue;
|
||||
record[record.length] = newPair;
|
||||
}
|
||||
hash.Insert(aliasToAdd, aliasValue);
|
||||
AliasService(class'AliasService'.static.Require()).PendingSaveSource(self);
|
||||
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 final function RemoveAlias(string aliasToRemove)
|
||||
{
|
||||
local int i;
|
||||
local bool removedAliasFromRecord;
|
||||
hash.Remove(aliasToRemove);
|
||||
while (i < record.length)
|
||||
{
|
||||
if (record[i].alias ~= aliasToRemove)
|
||||
{
|
||||
record.Remove(i, 1);
|
||||
removedAliasFromRecord = true;
|
||||
}
|
||||
else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < loadedAliasObjects.length; i += 1) {
|
||||
loadedAliasObjects[i].RemoveAlias(aliasToRemove);
|
||||
}
|
||||
if (removedAliasFromRecord)
|
||||
{
|
||||
AliasService(class'AliasService'.static.Require())
|
||||
.PendingSaveSource(self);
|
||||
}
|
||||
}
|
||||
|
||||
// Performs initial hashing of every record with valid alias.
|
||||
// In case of duplicate or invalid aliases - method will skip them
|
||||
// and log warnings.
|
||||
private final function HashValidAliases()
|
||||
{
|
||||
if (hash == none) {
|
||||
_.logger.Warning("Alias source `" $ string(class) $ "` called"
|
||||
$ "`HashValidAliases()` function without creating an `AliasHasher`"
|
||||
$ "instance first. This should not have happened.");
|
||||
return;
|
||||
}
|
||||
HashValidAliasesFromRecord();
|
||||
HashValidAliasesFromPerObjectConfig();
|
||||
}
|
||||
|
||||
private final function LogDuplicateAliasWarning(
|
||||
string alias,
|
||||
string existingValue)
|
||||
{
|
||||
_.logger.Warning("Alias source `" $ string(class)
|
||||
$ "` has duplicate record for alias \"" $ alias
|
||||
$ "\". This is likely due to an erroneous config. \"" $ existingValue
|
||||
$ "\" value will be used.");
|
||||
}
|
||||
|
||||
private final function LogInvalidAliasWarning(string invalidAlias)
|
||||
{
|
||||
_.logger.Warning("Alias source `" $ string(class)
|
||||
$ "` contains invalid alias name \"" $ invalidAlias
|
||||
$ "\". This alias will not be loaded.");
|
||||
}
|
||||
|
||||
private final function HashValidAliasesFromRecord()
|
||||
{
|
||||
local int i;
|
||||
local bool isDuplicate;
|
||||
local string existingValue;
|
||||
for (i = 0; i < record.length; i += 1)
|
||||
{
|
||||
if (!_.alias.IsAliasValid(record[i].alias))
|
||||
{
|
||||
LogInvalidAliasWarning(record[i].alias);
|
||||
continue;
|
||||
}
|
||||
isDuplicate = !hash.InsertIfMissing(record[i].alias, record[i].value,
|
||||
existingValue);
|
||||
if (isDuplicate) {
|
||||
LogDuplicateAliasWarning(record[i].alias, existingValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final function HashValidAliasesFromPerObjectConfig()
|
||||
{
|
||||
local int i, j;
|
||||
local bool isDuplicate;
|
||||
local string existingValue;
|
||||
local string objectValue;
|
||||
local array<string> objectAliases;
|
||||
for (i = 0; i < loadedAliasObjects.length; i += 1)
|
||||
{
|
||||
objectValue = loadedAliasObjects[i].GetValue();
|
||||
objectAliases = loadedAliasObjects[i].GetAliases();
|
||||
for (j = 0; j < objectAliases.length; j += 1)
|
||||
{
|
||||
if (!_.alias.IsAliasValid(objectAliases[j]))
|
||||
{
|
||||
LogInvalidAliasWarning(objectAliases[j]);
|
||||
continue;
|
||||
}
|
||||
isDuplicate = !hash.InsertIfMissing(objectAliases[j], objectValue,
|
||||
existingValue);
|
||||
if (isDuplicate) {
|
||||
LogDuplicateAliasWarning(objectAliases[j], existingValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
private final function Aliases GetAliasesObjectWithValue(string value)
|
||||
{
|
||||
local int i;
|
||||
local Aliases newAliasesObject;
|
||||
// This method only makes sense if this `AliasSource` supports
|
||||
// per-object-config storage.
|
||||
if (aliasesClass == none)
|
||||
{
|
||||
_.logger.Warning("`GetAliasesObjectForValue()` function was called for "
|
||||
$ "alias source with `aliasesClass == none`."
|
||||
$ "This should not happen.");
|
||||
return none;
|
||||
}
|
||||
for (i = 0; i < loadedAliasObjects.length; i += 1)
|
||||
{
|
||||
if (loadedAliasObjects[i].GetValue() ~= value) {
|
||||
return loadedAliasObjects[i];
|
||||
}
|
||||
}
|
||||
newAliasesObject = new(none, value) aliasesClass;
|
||||
loadedAliasObjects[loadedAliasObjects.length] = newAliasesObject;
|
||||
return newAliasesObject;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
// Source main parameters
|
||||
configName = "AcediaAliases"
|
||||
aliasesClass = class'Aliases'
|
||||
// HashTable twice the size of data entries should do it
|
||||
HASH_TABLE_SCALE = 2.0
|
||||
}
|
@ -1,20 +1,14 @@
|
||||
/**
|
||||
* Aliases allow users to define human-readable and easier to use
|
||||
* "synonyms" to some symbol sequences (mainly names of UnrealScript classes).
|
||||
* Due to how aliases are stored, there is a limitation on original
|
||||
* values to which aliases refer: it must be a valid object name to store via
|
||||
* `perObjectConfig`. For example it cannot contain `]` or a dot `.`
|
||||
* (use `:` as a delimiter for class names: `KFMod:M14EBRBattleRifle`).
|
||||
* Aliases can be grouped into categories: "weapons", "test", "maps", etc.
|
||||
* Aliases can be configured in `AcediaAliases` in form:
|
||||
* ________________________________________________________________________
|
||||
* | [<groupName>/<aliasesValue> Aliases]
|
||||
* | Alias="<alias1>"
|
||||
* | Alias="<alias2>"
|
||||
* | ...
|
||||
* |_______________________________________________________________________
|
||||
* where <groupName>, <aliasesValue>, <alias1>, ... can be replaced with
|
||||
* desired values.
|
||||
* 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 2020 Anton Tarasenko
|
||||
*------------------------------------------------------------------------------
|
||||
* This file is part of Acedia.
|
||||
@ -36,105 +30,113 @@ class Aliases extends AcediaObject
|
||||
perObjectConfig
|
||||
config(AcediaAliases);
|
||||
|
||||
// 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<AliasSource> 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<string> alias;
|
||||
|
||||
// Since '.'s in values are converted into ':' for storage purposes,
|
||||
// we need methods to convert between "storage" and "actual" value version.
|
||||
// `ToStorageVersion()` and `ToActualVersion()` do that.
|
||||
private final function string ToStorageVersion(string actualValue)
|
||||
{
|
||||
return Repl(actualValue, ".", ":");
|
||||
}
|
||||
|
||||
private final function string ToActualVersion(string storageValue)
|
||||
{
|
||||
return Repl(storageValue, ":", ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* All data is stored in config as a bunch of named `Aliases` objects
|
||||
* (via `perObjectConfig`). Name of each object records both aliases group and
|
||||
* value (see class description for details).
|
||||
* Aliases themselves are recorded into the `alias` array.
|
||||
* Returns value that caller's `Aliases` object's aliases point to.
|
||||
*
|
||||
* @return Value, stored by this object.
|
||||
*/
|
||||
|
||||
// Stores name of the configuration file.
|
||||
var private const string configName;
|
||||
// Both value
|
||||
// Symbol (or symbol sequence) that separates value from the group in
|
||||
// `[<groupName>/<aliasesValue> Aliases]`.
|
||||
var private const string delimiter;
|
||||
|
||||
// Set once to prevent more than one object loading.
|
||||
var private bool initialized;
|
||||
|
||||
// All aliases objects, specified by the configuration file.
|
||||
var private array<Aliases> availableRecords;
|
||||
|
||||
// Data loaded from the configuration file into the `Aliases` object.
|
||||
// Value to which all aliases refer to.
|
||||
var private string originalValue;
|
||||
// Group to which this object's aliases belong to.
|
||||
var private string groupName;
|
||||
// Recorded aliases ("synonyms") for the `originalValue`.
|
||||
var public config array<string> alias;
|
||||
|
||||
// Initializes data that we can not directly read from the configuration file.
|
||||
private final function Initialize()
|
||||
public final function string GetValue()
|
||||
{
|
||||
if (initialized) return;
|
||||
|
||||
availableRecords.length = 0;
|
||||
ParseObjectName(string(self.name));
|
||||
initialized = true;
|
||||
return ToActualVersion(string(self.name));
|
||||
}
|
||||
|
||||
private final function ParseObjectName(string configName)
|
||||
/**
|
||||
* 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<string> GetAliases()
|
||||
{
|
||||
local int i;
|
||||
local array<string> splitName;
|
||||
Split(configName, "/", splitName);
|
||||
groupName = splitName[0];
|
||||
originalValue = "";
|
||||
for (i = 1; i < splitName.length; i += 1)
|
||||
{
|
||||
originalValue $= splitName[i];
|
||||
return alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* [For inner use by `AliasSource`] Adds new alias to this object.
|
||||
*
|
||||
* Does no duplicates checks through for it's `AliasSource` and
|
||||
* neither it updates relevant `AliasHash`,
|
||||
* but will prevent adding duplicate records inside it's own storage.
|
||||
*
|
||||
* @param aliasToAdd Alias to add to caller `Aliases` object.
|
||||
*/
|
||||
public final function AddAlias(string aliasToAdd)
|
||||
{
|
||||
local int i;
|
||||
for (i = 0; i < alias.length; i += 1) {
|
||||
if (alias[i] ~= aliasToAdd) return;
|
||||
}
|
||||
alias[alias.length] = ToStorageVersion(aliasToAdd);
|
||||
AliasService(class'AliasService'.static.Require())
|
||||
.PendingSaveObject(self);
|
||||
}
|
||||
|
||||
// This function loads all the defined aliases from the config file.
|
||||
// Need to only be called once, further calls do nothing.
|
||||
public static final function LoadAliases()
|
||||
/**
|
||||
* [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(string aliasToRemove)
|
||||
{
|
||||
local int i;
|
||||
local array<string> recordNames;
|
||||
if (default.initialized) return;
|
||||
recordNames =
|
||||
GetPerObjectNames(default.configName, string(class'Aliases'.name));
|
||||
for (i = 0; i < recordNames.length; i += 1)
|
||||
local int i;
|
||||
local bool removedAlias;
|
||||
while (i < alias.length)
|
||||
{
|
||||
default.availableRecords[i] = new(none, recordNames[i]) class'Aliases';
|
||||
if (default.availableRecords[i] != none)
|
||||
if (alias[i] ~= aliasToRemove)
|
||||
{
|
||||
default.availableRecords[i].Initialize();
|
||||
alias.Remove(i, 1);
|
||||
removedAlias = true;
|
||||
}
|
||||
else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
default.initialized = true;
|
||||
if (removedAlias)
|
||||
{
|
||||
AliasService(class'AliasService'.static.Require())
|
||||
.PendingSaveObject(self);
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to find original value for a given alias in a given group.
|
||||
public static final function bool ResolveAlias
|
||||
(
|
||||
string group,
|
||||
string alias,
|
||||
out string result
|
||||
)
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
local int i, j;
|
||||
if (!default.initialized) return false;
|
||||
for (i = 0; i < default.availableRecords.length; i += 1)
|
||||
{
|
||||
if (!(default.availableRecords[i].groupName ~= group)) continue;
|
||||
for (j = 0; j < default.availableRecords[i].alias.length; j += 1)
|
||||
{
|
||||
if (default.availableRecords[i].alias[j] ~= alias)
|
||||
{
|
||||
result = default.availableRecords[i].originalValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (alias.length <= 0) {
|
||||
ClearConfig();
|
||||
}
|
||||
else {
|
||||
SaveConfig();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
initialized = false
|
||||
configName = "AcediaAliases"
|
||||
delimiter = "/"
|
||||
sourceClass = class'AliasSource'
|
||||
}
|
@ -19,21 +19,211 @@
|
||||
*/
|
||||
class AliasesAPI extends Singleton;
|
||||
|
||||
// Resolves original value for given alias and it's group.
|
||||
// Returns `false` if there no such alias and `true` if there is.
|
||||
public function bool Resolve(string group, string alias, out string result)
|
||||
/**
|
||||
* Checks that passed value is a valid alias name.
|
||||
*
|
||||
* A valid name is any name consisting out of 128 ASCII symbols.
|
||||
*
|
||||
* @param aliasToCheck Alias to check for validity.
|
||||
* @return `true` if `aliasToCheck` is a valid alias and `false` otherwise.
|
||||
*/
|
||||
public final function bool IsAliasValid(string aliasToCheck)
|
||||
{
|
||||
return class'Aliases'.static.ResolveAlias(group, alias, result);
|
||||
return _.text.IsASCIIString(aliasToCheck);
|
||||
}
|
||||
|
||||
// Tries to resolve given alias.
|
||||
// If fails - returns passed `alias` value back.
|
||||
public function string Try(string group, string alias)
|
||||
/**
|
||||
* Provides an easier access to the instance of the `AliasSource` of
|
||||
* the given class.
|
||||
*
|
||||
* Can fail if `customSourceClass` is incorrectly defined.
|
||||
*
|
||||
* @param customSourceClass Class of the source we want.
|
||||
* @return Instance of the requested `AliasSource`,
|
||||
* `none` if `customSourceClass` is incorrectly defined.
|
||||
*/
|
||||
public final function AliasSource GetCustomSource(
|
||||
class<AliasSource> customSourceClass)
|
||||
{
|
||||
local string result;
|
||||
if (class'Aliases'.static.ResolveAlias(group, alias, result))
|
||||
{
|
||||
return result;
|
||||
return AliasSource(customSourceClass.static.GetInstance(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `AliasSource` 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.
|
||||
* Can return `none` if no source for weapons was configured or
|
||||
* the configured source is incorrectly defined.
|
||||
*/
|
||||
public final function AliasSource GetWeaponSource()
|
||||
{
|
||||
local AliasSource weaponSource;
|
||||
local class<AliasSource> sourceClass;
|
||||
sourceClass = class'AliasService'.default.weaponAliasesSource;
|
||||
if (sourceClass == none) {
|
||||
_.logger.Failure("No weapon aliases source configured for Acedia's"
|
||||
@ "alias API. Error is most likely cause by erroneous config.");
|
||||
return none;
|
||||
}
|
||||
weaponSource = AliasSource(sourceClass.static.GetInstance(true));
|
||||
if (weaponSource == none) {
|
||||
_.logger.Failure("`AliasSource` class `" $ string(sourceClass) $ "` 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`.");
|
||||
return none;
|
||||
}
|
||||
return weaponSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `AliasSource` 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.
|
||||
* Can return `none` if no source for colors was configured or
|
||||
* the configured source is incorrectly defined.
|
||||
*/
|
||||
public final function AliasSource GetColorSource()
|
||||
{
|
||||
local AliasSource colorSource;
|
||||
local class<AliasSource> sourceClass;
|
||||
sourceClass = class'AliasService'.default.colorAliasesSource;
|
||||
if (sourceClass == none) {
|
||||
_.logger.Failure("No color aliases source configured for Acedia's"
|
||||
@ "alias API. Error is most likely cause by erroneous config.");
|
||||
return none;
|
||||
}
|
||||
colorSource = AliasSource(sourceClass.static.GetInstance(true));
|
||||
if (colorSource == none) {
|
||||
_.logger.Failure("`AliasSource` class `" $ string(sourceClass) $ "` 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`.");
|
||||
return none;
|
||||
}
|
||||
return colorSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to look up a value, stored for given alias in an `AliasSource`
|
||||
* configured to store weapon aliases. Reports error on failure.
|
||||
*
|
||||
* Lookup of alias can fail if either alias does not exist in weapon alias
|
||||
* source or weapon alias source itself does not exist
|
||||
* (due to either faulty configuration or incorrect definition).
|
||||
* To determine if weapon alias source exists you can check
|
||||
* `_.alias.GetWeaponSource()` value.
|
||||
*
|
||||
* Also see `TryWeapon()` method.
|
||||
*
|
||||
* @param alias Alias, for which method will attempt to look up a value.
|
||||
* Case-insensitive.
|
||||
* @param value If passed `alias` was recorded as a weapon alias,
|
||||
* it's corresponding value will be written in this variable.
|
||||
* Otherwise value is undefined.
|
||||
* @return `true` if lookup was successful and `false` otherwise.
|
||||
*/
|
||||
public final function bool ResolveWeapon(string alias, out string result)
|
||||
{
|
||||
local AliasSource source;
|
||||
source = GetWeaponSource();
|
||||
if (source != none) {
|
||||
return source.Resolve(alias, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to look up a value, stored for given alias in an `AliasSource`
|
||||
* configured to store weapon aliases and silently returns given `alias`
|
||||
* value upon failure.
|
||||
*
|
||||
* Lookup of alias can fail if either alias does not exist in weapon alias
|
||||
* source or weapon alias source itself does not exist
|
||||
* (due to either faulty configuration or incorrect definition).
|
||||
* To determine if weapon alias source exists you can check
|
||||
* `_.alias.GetWeaponSource()` value.
|
||||
*
|
||||
* Also see `ResolveWeapon()` method.
|
||||
*
|
||||
* @param alias Alias, for which method will attempt to look up a value.
|
||||
* Case-insensitive.
|
||||
* @return Weapon value corresponding to a given alias, if it was present in
|
||||
* the weapon alias source and value of `alias` parameter instead.
|
||||
*/
|
||||
public function string TryWeapon(string alias)
|
||||
{
|
||||
local AliasSource source;
|
||||
source = GetWeaponSource();
|
||||
if (source != none) {
|
||||
return source.Try(alias);
|
||||
}
|
||||
return alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to look up a value, stored for given alias in an `AliasSource`
|
||||
* configured to store color aliases. Reports error on failure.
|
||||
*
|
||||
* Lookup of alias can fail if either alias does not exist in color alias
|
||||
* source or color alias source itself does not exist
|
||||
* (due to either faulty configuration or incorrect definition).
|
||||
* To determine if color alias source exists you can check
|
||||
* `_.alias.GetColorSource()` value.
|
||||
*
|
||||
* Also see `TryColor()` method.
|
||||
*
|
||||
* @param alias Alias, for which method will attempt to look up a value.
|
||||
* Case-insensitive.
|
||||
* @param value If passed `alias` was recorded as a color alias,
|
||||
* it's corresponding value will be written in this variable.
|
||||
* Otherwise value is undefined.
|
||||
* @return `true` if lookup was successful and `false` otherwise.
|
||||
*/
|
||||
public final function bool ResolveColor(string alias, out string result)
|
||||
{
|
||||
local AliasSource source;
|
||||
source = GetColorSource();
|
||||
if (source != none) {
|
||||
return source.Resolve(alias, result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to look up a value, stored for given alias in an `AliasSource`
|
||||
* configured to store color aliases and silently returns given `alias`
|
||||
* value upon failure.
|
||||
*
|
||||
* Lookup of alias can fail if either alias does not exist in color alias
|
||||
* source or color alias source itself does not exist
|
||||
* (due to either faulty configuration or incorrect definition).
|
||||
* To determine if color alias source exists you can check
|
||||
* `_.alias.GetColorSource()` value.
|
||||
*
|
||||
* Also see `ResolveColor()` method.
|
||||
*
|
||||
* @param alias Alias, for which method will attempt to look up a value.
|
||||
* Case-insensitive.
|
||||
* @return Color value corresponding to a given alias, if it was present in
|
||||
* the color alias source and value of `alias` parameter instead.
|
||||
*/
|
||||
public function string TryColor(string alias)
|
||||
{
|
||||
local AliasSource source;
|
||||
source = GetColorSource();
|
||||
if (source != none) {
|
||||
return source.Try(alias);
|
||||
}
|
||||
return alias;
|
||||
}
|
||||
|
27
sources/Core/Aliases/BuiltInSources/ColorAliasSource.uc
Normal file
27
sources/Core/Aliases/BuiltInSources/ColorAliasSource.uc
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Source intended for color 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class ColorAliasSource extends AliasSource
|
||||
config(AcediaAliases_Colors);
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
configName = "AcediaAliases_Colors"
|
||||
aliasesClass = class'ColorAliases'
|
||||
}
|
27
sources/Core/Aliases/BuiltInSources/ColorAliases.uc
Normal file
27
sources/Core/Aliases/BuiltInSources/ColorAliases.uc
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Per-object-configuration intended for color 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class ColorAliases extends Aliases
|
||||
perObjectConfig
|
||||
config(AcediaAliases_Colors);
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
sourceClass = class'ColorAliasSource'
|
||||
}
|
27
sources/Core/Aliases/BuiltInSources/WeaponAliasSource.uc
Normal file
27
sources/Core/Aliases/BuiltInSources/WeaponAliasSource.uc
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Source intended for weapon 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class WeaponAliasSource extends AliasSource
|
||||
config(AcediaAliases_Weapons);
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
configName = "AcediaAliases_Weapons"
|
||||
aliasesClass = class'WeaponAliases'
|
||||
}
|
27
sources/Core/Aliases/BuiltInSources/WeaponAliases.uc
Normal file
27
sources/Core/Aliases/BuiltInSources/WeaponAliases.uc
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Per-object-configuration intended for weapon 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class WeaponAliases extends Aliases
|
||||
perObjectConfig
|
||||
config(AcediaAliases_Weapons);
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
sourceClass = class'WeaponAliasSource'
|
||||
}
|
Reference in New Issue
Block a user