Browse Source
Aliases were initially using `Actor`-base objects to manage themselves, but that became a legacy approachin AcediaCore and they had to be reimplemented without any `Actor`s (based on `Feature` instead), allowing them mto be usable even when no `LevelCore` is provided.pull/8/head
Anton Tarasenko
2 years ago
25 changed files with 1391 additions and 802 deletions
@ -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" |
||||
; 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') |
@ -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" |
@ -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 <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; |
||||
var public config const class<AliasSource> featureAliasesSource; |
||||
var public config const class<AliasSource> 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' |
||||
} |
@ -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 |
||||
* "<something>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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
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<string> 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<AliasSource> 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<AliasesStorage> LoadAllObjects() |
||||
{ |
||||
local int i; |
||||
local array<string> objectNames; |
||||
local array<AliasesStorage> 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<Text> GetAliases() |
||||
{ |
||||
local int i; |
||||
local array<Text> 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" |
||||
} |
@ -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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class Aliases_Feature extends Feature |
||||
dependson(Aliases); |
||||
|
||||
struct ClassSourcePair |
||||
{ |
||||
var class<AliasSource> 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<ClassSourcePair> 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<AliasSource> 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<Aliases.CustomSourceRecord> 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.") |
||||
} |
@ -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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
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 `$<alias_name>`. |
||||
* `<alias_name>` can only contain ASCII latin letters and digits. |
||||
* `<alias_name>` 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 `<alias_name>` 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 `<alias_name>`, 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<Text> 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<string> GetAliases_S(string value) |
||||
{ |
||||
local int i; |
||||
local Text valueAsText; |
||||
local array<Text> resultWithTexts; |
||||
local array<string> 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 |
||||
{ |
||||
} |
Loading…
Reference in new issue