dkanus
2 years ago
40 changed files with 1678 additions and 510 deletions
@ -0,0 +1,277 @@ |
|||||||
|
/** |
||||||
|
* Author: dkanus |
||||||
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore |
||||||
|
* License: GPL |
||||||
|
* Copyright 2022-2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class SideEffect extends AcediaObject; |
||||||
|
|
||||||
|
//! Defines the concept of "side effects" in the context of the Acedia and its derivative mods. |
||||||
|
//! |
||||||
|
//! Side effects are changes that are not part of the mod's main functionality, but rather something |
||||||
|
//! necessary to enable that functionality, while also possibly affecting how other mods work. |
||||||
|
//! Documenting these side effects helps developers and server admins understand changes performed |
||||||
|
//! by Acedia or mods based on it, and anticipate any potential conflicts or issues that may arise. |
||||||
|
//! |
||||||
|
//! It should be noted that what constitutes a side effect is loosely defined, and it is simply |
||||||
|
//! a tool to inform others that something unexpected has happened, possibly breaking other mods. |
||||||
|
//! AcediaCore aims to leave a minimal footprint, but still needs to make some changes |
||||||
|
//! (e.g., adding GameRules, patching code of some functions), and [`SideEffects`] can be used to |
||||||
|
//! document them. |
||||||
|
//! Similarly, [`SideEffect`]s can be used to document changes made by AcediaFixes, a package meant |
||||||
|
//! only for fixing bugs that inevitably needs to make many under-the-hood changes to achieve |
||||||
|
//! that goal. |
||||||
|
//! |
||||||
|
//! On the other hand gameplay mods like Futility or Ire can make a lot of changes, but they can all |
||||||
|
//! be just expected part of its direct functionality: we expect feature that shares dosh of leavers |
||||||
|
//! to alter players' dosh values, so this is not a side effect. |
||||||
|
//! Such mods are likely not going to have to specify any side effects whatsoever. |
||||||
|
|
||||||
|
var private Text name; |
||||||
|
var private Text description; |
||||||
|
var private Text package; |
||||||
|
var private Text source; |
||||||
|
var private Text status; |
||||||
|
var private bool initialized; |
||||||
|
|
||||||
|
protected function Finalizer() { |
||||||
|
_.memory.Free(name); |
||||||
|
_.memory.Free(description); |
||||||
|
_.memory.Free(package); |
||||||
|
_.memory.Free(source); |
||||||
|
_.memory.Free(status); |
||||||
|
name = none; |
||||||
|
description = none; |
||||||
|
package = none; |
||||||
|
source = none; |
||||||
|
status = none; |
||||||
|
initialized = false; |
||||||
|
} |
||||||
|
|
||||||
|
/// Checks whether caller [`SideEffect`] was initialized. |
||||||
|
/// |
||||||
|
/// Initialization must happen directly after creation and only initialized instances should |
||||||
|
/// ever be used. |
||||||
|
public final function bool IsInitialized() { |
||||||
|
return initialized; |
||||||
|
} |
||||||
|
|
||||||
|
/// This function is used to set the initial values of the [`SideEffect`] object properties when it |
||||||
|
/// is first created. |
||||||
|
/// |
||||||
|
/// All arguments must be not `none`. |
||||||
|
/// |
||||||
|
/// Returns `true` if the initialization was successful, `false` otherwise (including the case where |
||||||
|
/// the [`SideEffect`] object has already been initialized). |
||||||
|
public final function bool Initialize( |
||||||
|
BaseText sideEffectName, |
||||||
|
BaseText sideEffectDescription, |
||||||
|
BaseText sideEffectPackage, |
||||||
|
BaseText sideEffectSource, |
||||||
|
BaseText sideEffectStatus |
||||||
|
) { |
||||||
|
if (initialized) return false; |
||||||
|
if (sideEffectName == none) return false; |
||||||
|
if (sideEffectDescription == none) return false; |
||||||
|
if (sideEffectPackage == none) return false; |
||||||
|
if (sideEffectSource == none) return false; |
||||||
|
if (sideEffectStatus == none) return false; |
||||||
|
|
||||||
|
name = sideEffectName.Copy(); |
||||||
|
description = sideEffectDescription.Copy(); |
||||||
|
package = sideEffectPackage.Copy(); |
||||||
|
source = sideEffectSource.Copy(); |
||||||
|
status = sideEffectStatus.Copy(); |
||||||
|
initialized = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// This function is used to set the initial values of the [`SideEffect`] object properties when it |
||||||
|
/// is first created. |
||||||
|
/// |
||||||
|
/// Returns `true` if the initialization was successful, `false` otherwise (including the case where |
||||||
|
/// the [`SideEffect`] object has already been initialized). |
||||||
|
public final function bool Initialize_S( |
||||||
|
string sideEffectName, |
||||||
|
string sideEffectDescription, |
||||||
|
string sideEffectPackage, |
||||||
|
string sideEffectSource, |
||||||
|
string sideEffectStatus |
||||||
|
) { |
||||||
|
name = _.text.FromString(sideEffectName); |
||||||
|
description = _.text.FromString(sideEffectDescription); |
||||||
|
package = _.text.FromString(sideEffectPackage); |
||||||
|
source = _.text.FromString(sideEffectSource); |
||||||
|
status = _.text.FromString(sideEffectStatus); |
||||||
|
initialized = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a brief summary that conveys the purpose of the caller [SideEffect] to the user in |
||||||
|
/// a clear and concise manner. |
||||||
|
/// |
||||||
|
/// While there is no hard limit on the length of this value, it is recommended to keep it under 80 |
||||||
|
/// characters for readability. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetName() { |
||||||
|
if (initialized) { |
||||||
|
return name.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a brief summary that conveys the purpose of the caller [SideEffect] to the user in |
||||||
|
/// a clear and concise manner. |
||||||
|
/// |
||||||
|
/// While there is no hard limit on the length of this value, it is recommended to keep it under 80 |
||||||
|
/// characters for readability. |
||||||
|
public final function string GetName_S() { |
||||||
|
if (initialized && name != none) { |
||||||
|
return name.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the detailed description of the caller [`SideEffect`], which describes what was done |
||||||
|
/// and why the relevant change was necessary. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetDescription() { |
||||||
|
if (initialized) { |
||||||
|
return description.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the detailed description of the caller [`SideEffect`], which describes what was done |
||||||
|
/// and why the relevant change was necessary. |
||||||
|
public final function string GetDescription_S() { |
||||||
|
if (initialized && description != none) { |
||||||
|
return description.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the name of the package ("*.u" file) that introduced the changes |
||||||
|
/// represented by the caller `SideEffect`. |
||||||
|
/// |
||||||
|
/// It should be noted that even if a different package requested the functionality that led to |
||||||
|
/// the changes being made, the package responsible for the side effect is the one that performed |
||||||
|
/// the changes. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetPackage() { |
||||||
|
if (initialized) { |
||||||
|
return package.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the name of the package ("*.u" file) that introduced the changes |
||||||
|
/// represented by the caller `SideEffect`. |
||||||
|
/// |
||||||
|
/// It should be noted that even if a different package requested the functionality that led to |
||||||
|
/// the changes being made, the package responsible for the side effect is the one that performed |
||||||
|
/// the changes. |
||||||
|
public final function string GetPackage_S() { |
||||||
|
if (initialized && package != none) { |
||||||
|
return package.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// The origin of this change within the package is specified, and for larger packages, additional |
||||||
|
/// details can be provided to clarify the cause of the change. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetSource() { |
||||||
|
if (initialized) { |
||||||
|
return source.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// The origin of this change within the package is specified, and for larger packages, additional |
||||||
|
/// details can be provided to clarify the cause of the change. |
||||||
|
public final function string GetSource_S() { |
||||||
|
if (initialized && source != none) { |
||||||
|
return source.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
/// The status of the caller [`SideEffect`], that is used to differentiate between different ways |
||||||
|
/// that a side effect may have been introduced, allowing for better tracking and management of |
||||||
|
/// the effect. |
||||||
|
/// |
||||||
|
/// Returned value for initialized [`SideEffect`] is guaranteed to not be `none`, as it is |
||||||
|
/// a required property of the [`SideEffect`] object. |
||||||
|
public final function Text GetStatus() { |
||||||
|
if (initialized) { |
||||||
|
return status.Copy(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/// The status of the caller [`SideEffect`], that is used to differentiate between different ways |
||||||
|
/// that a side effect may have been introduced, allowing for better tracking and management of |
||||||
|
/// the effect. |
||||||
|
public final function string GetStatus_S() { |
||||||
|
if (initialized && status != none) { |
||||||
|
return status.ToString(); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
public function bool IsEqual(Object other) { |
||||||
|
local SideEffect otherSideEffect; |
||||||
|
|
||||||
|
if (self == other) return true; |
||||||
|
otherSideEffect = SideEffect(other); |
||||||
|
if (otherSideEffect == none) return false; |
||||||
|
if (!otherSideEffect.initialized) return false; |
||||||
|
if (GetHashCode() != otherSideEffect.GetHashCode()) return false; |
||||||
|
if (!name.Compare(otherSideEffect.name,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!package.Compare(otherSideEffect.package,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!source.Compare(otherSideEffect.source,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!status.Compare(otherSideEffect.status,, SFORM_SENSITIVE)) return false; |
||||||
|
if (!description.Compare(otherSideEffect.description,, SFORM_SENSITIVE)) return false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
protected function int CalculateHashCode() { |
||||||
|
local int result; |
||||||
|
|
||||||
|
if (initialized) { |
||||||
|
result = name.GetHashCode(); |
||||||
|
result = CombineHash(result, description.GetHashCode()); |
||||||
|
result = CombineHash(result, package.GetHashCode()); |
||||||
|
result = CombineHash(result, source.GetHashCode()); |
||||||
|
result = CombineHash(result, status.GetHashCode()); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,194 @@ |
|||||||
|
/** |
||||||
|
* Author: dkanus |
||||||
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore |
||||||
|
* License: GPL |
||||||
|
* Copyright 2022-2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class SideEffectAPI extends AcediaObject; |
||||||
|
|
||||||
|
var private array<SideEffect> activeSideEffects; |
||||||
|
|
||||||
|
/// Returns an array containing all SideEffect objects that have been registered up to this point. |
||||||
|
/// |
||||||
|
/// The order of the elements in the array is not guaranteed. |
||||||
|
public function array<SideEffect> GetAll() { |
||||||
|
local int i; |
||||||
|
|
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
activeSideEffects[i].NewRef(); |
||||||
|
} |
||||||
|
return activeSideEffects; |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns all registered [`SideEffects`] that are associated with the specified package name |
||||||
|
/// (case-insensitive). |
||||||
|
public function array<SideEffect> GetFromPackage(BaseText packageName) { |
||||||
|
local int i; |
||||||
|
local Text nextPackage; |
||||||
|
local array<SideEffect> result; |
||||||
|
|
||||||
|
if (packageName == none) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
nextPackage = activeSideEffects[i].GetPackage(); |
||||||
|
if (packageName.Compare(nextPackage, SCASE_INSENSITIVE)) { |
||||||
|
activeSideEffects[i].NewRef(); |
||||||
|
result[result.length] = activeSideEffects[i]; |
||||||
|
} |
||||||
|
_.memory.Free(nextPackage); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Adds a new side effect to the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if any of its arguments are `none` or a side effect with that exact |
||||||
|
/// contents was already added. |
||||||
|
public function SideEffect Add( |
||||||
|
BaseText sideEffectName, |
||||||
|
BaseText sideEffectDescription, |
||||||
|
BaseText sideEffectPackage, |
||||||
|
BaseText sideEffectSource, |
||||||
|
BaseText sideEffectStatus |
||||||
|
) { |
||||||
|
local bool initialized; |
||||||
|
local SideEffect newSideEffect; |
||||||
|
|
||||||
|
newSideEffect = SideEffect(_.memory.Allocate(class'SideEffect')); |
||||||
|
initialized = newSideEffect.Initialize( |
||||||
|
sideEffectName, |
||||||
|
sideEffectDescription, |
||||||
|
sideEffectPackage, |
||||||
|
sideEffectSource, |
||||||
|
sideEffectStatus); |
||||||
|
if (initialized) { |
||||||
|
if (!AddInstance(newSideEffect)) { |
||||||
|
_.memory.Free(newSideEffect); |
||||||
|
return none; |
||||||
|
} |
||||||
|
} else { |
||||||
|
_.memory.Free(newSideEffect); |
||||||
|
return none; |
||||||
|
} |
||||||
|
return newSideEffect; |
||||||
|
} |
||||||
|
|
||||||
|
/// Adds a new side effect to the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if a side effect with that exact contents was already added. |
||||||
|
public function SideEffect Add_S( |
||||||
|
string sideEffectName, |
||||||
|
string sideEffectDescription, |
||||||
|
string sideEffectPackage, |
||||||
|
string sideEffectSource, |
||||||
|
string sideEffectStatus |
||||||
|
) { |
||||||
|
local bool initialized; |
||||||
|
local SideEffect newSideEffect; |
||||||
|
|
||||||
|
newSideEffect = SideEffect(_.memory.Allocate(class'SideEffect')); |
||||||
|
initialized = newSideEffect.Initialize_S( |
||||||
|
sideEffectName, |
||||||
|
sideEffectDescription, |
||||||
|
sideEffectPackage, |
||||||
|
sideEffectSource, |
||||||
|
sideEffectStatus); |
||||||
|
if (initialized) { |
||||||
|
if (!AddInstance(newSideEffect)) { |
||||||
|
_.memory.Free(newSideEffect); |
||||||
|
return none; |
||||||
|
} |
||||||
|
} else { |
||||||
|
return none; |
||||||
|
} |
||||||
|
return newSideEffect; |
||||||
|
} |
||||||
|
|
||||||
|
/// Adds a new side effect to the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if its argument is `none`, non-initialized or a side effect with that |
||||||
|
/// exact contents was already added. |
||||||
|
public function bool AddInstance(SideEffect newSideEffect) { |
||||||
|
local int i; |
||||||
|
|
||||||
|
if (newSideEffect == none) return false; |
||||||
|
if (!newSideEffect.IsInitialized()) return false; |
||||||
|
|
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
if (activeSideEffects[i].IsEqual(newSideEffect)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
newSideEffect.NewRef(); |
||||||
|
activeSideEffects[activeSideEffects.length] = newSideEffect; |
||||||
|
LogAddingSideEffectChange(newSideEffect, true); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// Removes a side effect from the list of active side effects. |
||||||
|
/// |
||||||
|
/// This method will fail if its argument is `none`, non-initialized or a side effect with its |
||||||
|
/// contents isn't in the records. |
||||||
|
public function bool RemoveInstance(SideEffect inactiveSideEffect) { |
||||||
|
local int i; |
||||||
|
local bool foundInstance; |
||||||
|
|
||||||
|
if (inactiveSideEffect == none) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
for (i = 0; i < activeSideEffects.length; i += 1) { |
||||||
|
if (activeSideEffects[i].IsEqual(inactiveSideEffect)) { |
||||||
|
LogAddingSideEffectChange(activeSideEffects[i], false); |
||||||
|
_.memory.Free(activeSideEffects[i]); |
||||||
|
activeSideEffects.Remove(i, 1); |
||||||
|
foundInstance = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return foundInstance; |
||||||
|
} |
||||||
|
|
||||||
|
private function LogAddingSideEffectChange(SideEffect effect, bool added) { |
||||||
|
local MutableText builder; |
||||||
|
local Text sideEffectData; |
||||||
|
|
||||||
|
if (effect == none) { |
||||||
|
return; |
||||||
|
} |
||||||
|
builder = _.text.Empty(); |
||||||
|
if (added) { |
||||||
|
builder.Append(P("NEW SIDE EFFECT: ")); |
||||||
|
} else { |
||||||
|
builder.Append(P("REMOVED SIDE EFFECT: ")); |
||||||
|
} |
||||||
|
sideEffectData = effect.GetName(); |
||||||
|
builder.Append(sideEffectData); |
||||||
|
_.memory.Free(sideEffectData); |
||||||
|
sideEffectData = effect.GetStatus(); |
||||||
|
if (sideEffectData != none) { |
||||||
|
builder.Append(P(" {")); |
||||||
|
builder.Append(sideEffectData); |
||||||
|
_.memory.Free(sideEffectData); |
||||||
|
builder.Append(P("}")); |
||||||
|
} |
||||||
|
_.logger.Info(builder); |
||||||
|
builder.FreeSelf(); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class FunctionReplacement extends AcediaObject; |
||||||
|
|
||||||
|
var private Text replaced; |
||||||
|
var private Text replacer; |
||||||
|
var private SideEffect effect; |
||||||
|
|
||||||
|
protected function Finalizer() { |
||||||
|
_.memory.Free(replaced); |
||||||
|
_.memory.Free(replacer); |
||||||
|
_.memory.Free(effect); |
||||||
|
replaced = none; |
||||||
|
replacer = none; |
||||||
|
effect = none; |
||||||
|
} |
||||||
|
|
||||||
|
public static final function FunctionReplacement Make( |
||||||
|
BaseText oldFunction, |
||||||
|
BaseText newFunction, |
||||||
|
SideEffect sideEffect |
||||||
|
) { |
||||||
|
local FunctionReplacement newReplacement; |
||||||
|
|
||||||
|
if (oldFunction == none) return none; |
||||||
|
if (newFunction == none) return none; |
||||||
|
if (sideEffect == none) return none; |
||||||
|
|
||||||
|
newReplacement = FunctionReplacement(__().memory.Allocate(class'FunctionReplacement')); |
||||||
|
newReplacement.replaced = oldFunction.Copy(); |
||||||
|
newReplacement.replacer = newFunction.Copy(); |
||||||
|
sideEffect.NewRef(); |
||||||
|
newReplacement.effect = sideEffect; |
||||||
|
return newReplacement; |
||||||
|
} |
||||||
|
|
||||||
|
public final function Text GetReplacedFunctionName() { |
||||||
|
return replaced.Copy(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function string GetReplacedFunctionName_S() { |
||||||
|
return replaced.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function Text GetReplacerFunctionName() { |
||||||
|
return replacer.Copy(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function string GetReplacerFunctionName_S() { |
||||||
|
return replacer.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
public final function SideEffect GetSideEffect() { |
||||||
|
effect.NewRef(); |
||||||
|
return effect; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class MockInitialClass extends AcediaObject; |
||||||
|
|
||||||
|
var public int counter; |
||||||
|
|
||||||
|
public final function int DoIt() { |
||||||
|
counter += 1; |
||||||
|
return counter; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class MockReplacerClass extends MockInitialClass; |
||||||
|
|
||||||
|
public final function int DoIt2() { |
||||||
|
counter += 2; |
||||||
|
return -counter; |
||||||
|
} |
||||||
|
|
||||||
|
public final function int DoIt3() { |
||||||
|
counter -= 1; |
||||||
|
return 7; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
/** |
||||||
|
* Set of tests for `Command` class. |
||||||
|
* Copyright 2021 - 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 TEST_Unflect extends TestCase |
||||||
|
abstract; |
||||||
|
|
||||||
|
protected static function TESTS() { |
||||||
|
local MockInitialClass obj; |
||||||
|
|
||||||
|
obj = MockInitialClass(__().memory.Allocate(class'MockInitialClass')); |
||||||
|
Context("Replacing functions with `UnflectApi`"); |
||||||
|
Test_InitialReplacement(obj); |
||||||
|
Test_SecondReplacement(obj); |
||||||
|
Test_ReplacementWithSelf(obj); |
||||||
|
Test_RevertingReplacement(obj); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_InitialReplacement(MockInitialClass obj) { |
||||||
|
Issue("Functions aren't being replaced correctly the first time."); |
||||||
|
TEST_ExpectTrue(__().unflect.ReplaceFunction_S( |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"AcediaCore.MockReplacerClass.DoIt2", |
||||||
|
"testing")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == -2); |
||||||
|
TEST_ExpectTrue(obj.counter == 2); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == -4); |
||||||
|
TEST_ExpectTrue(obj.counter == 4); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_SecondReplacement(MockInitialClass obj) { |
||||||
|
Issue("Functions aren't being replaced correctly in case they were already replaced."); |
||||||
|
TEST_ExpectTrue(__().unflect.ReplaceFunction_S( |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"AcediaCore.MockReplacerClass.DoIt3", |
||||||
|
"testing")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 7); |
||||||
|
TEST_ExpectTrue(obj.counter == 3); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 7); |
||||||
|
TEST_ExpectTrue(obj.counter == 2); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_ReplacementWithSelf(MockInitialClass obj) { |
||||||
|
Issue("Attempting to replacing function with itself makes unexpected change."); |
||||||
|
TEST_ExpectFalse(__().unflect.ReplaceFunction_S( |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"AcediaCore.MockInitialClass.DoIt", |
||||||
|
"testing")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 7); |
||||||
|
TEST_ExpectTrue(obj.counter == 1); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_RevertingReplacement(MockInitialClass obj) { |
||||||
|
Issue("Reverting replaced function doesn't work."); |
||||||
|
TEST_ExpectTrue(__().unflect.RevertFunction_S("AcediaCore.MockInitialClass.DoIt")); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 2); |
||||||
|
TEST_ExpectTrue(obj.counter == 2); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 3); |
||||||
|
TEST_ExpectTrue(obj.counter == 3); |
||||||
|
|
||||||
|
Issue("Reverting already reverted function ends in success."); |
||||||
|
TEST_ExpectFalse(__().unflect.RevertFunction_S("AcediaCore.MockInitialClass.DoIt")); |
||||||
|
|
||||||
|
Issue("Reverting already reverted function leads to unexpected results."); |
||||||
|
TEST_ExpectTrue(obj.DoIt() == 4); |
||||||
|
TEST_ExpectTrue(obj.counter == 4); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
caseName = "Function replacement" |
||||||
|
caseGroup = "Unflect" |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 TypeCast extends Object; |
||||||
|
|
||||||
|
var Object nativeType; |
||||||
|
|
||||||
|
final function NativeCast(Object type) { |
||||||
|
nativeType = type; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UClass extends UState within Package; |
||||||
|
|
||||||
|
var int classFlags; |
||||||
|
var int classUnique; |
||||||
|
var Guid classGuid; |
||||||
|
var UClass classWithin; |
||||||
|
var name classConfigName; |
||||||
|
|
||||||
|
var array<struct RepRecord { |
||||||
|
var UProperty property; |
||||||
|
var int index; |
||||||
|
}> classReps; |
||||||
|
|
||||||
|
var array<UField> netFields; |
||||||
|
|
||||||
|
var array<struct Dependency { |
||||||
|
var UClass class; |
||||||
|
var int deep; |
||||||
|
var int scriptTextCRC; |
||||||
|
}> dependencies; |
||||||
|
|
||||||
|
var array<name> packageImports; |
||||||
|
var array<byte> defaults; |
||||||
|
var array<name> hideCategories; |
||||||
|
var array<name> dependentOn; |
||||||
|
|
||||||
|
var string defaultPropText; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UClassCast extends Object; |
||||||
|
|
||||||
|
var UClass nativeType; |
||||||
|
|
||||||
|
final function UClass Cast(Class type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UField extends Object |
||||||
|
abstract; |
||||||
|
|
||||||
|
var UField superField; |
||||||
|
var UField next; |
||||||
|
var UField hashNext; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UFieldCast extends Object; |
||||||
|
|
||||||
|
var UField nativeType; |
||||||
|
|
||||||
|
final function UField Cast(Field type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UFunction extends UStruct within UState |
||||||
|
dependson(Unflect); |
||||||
|
|
||||||
|
var byte functionMD5Digest[16]; |
||||||
|
|
||||||
|
var int functionFlags; |
||||||
|
var Unflect.Int16 nativeIndex; |
||||||
|
var Unflect.Int16 repOffset; |
||||||
|
var byte operPrecedence; |
||||||
|
|
||||||
|
var byte numParms; |
||||||
|
var Unflect.Int16 parmsSize; |
||||||
|
var Unflect.Int16 returnValueOffset; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UFunctionCast extends Object; |
||||||
|
|
||||||
|
var UFunction nativeType; |
||||||
|
|
||||||
|
final function UFunction Cast(Function type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UProperty extends UField within UField |
||||||
|
abstract; |
||||||
|
|
||||||
|
var int arrayDim; |
||||||
|
var int elementSize; |
||||||
|
var int propertyFlags; |
||||||
|
var name category; |
||||||
|
|
||||||
|
var byte repOffset[2]; |
||||||
|
var byte repIndex[2]; |
||||||
|
|
||||||
|
var transient int offset; |
||||||
|
var transient UProperty propertyLinkNext; |
||||||
|
var transient UProperty configLinkNext; |
||||||
|
var transient UProperty constructorLinkNext; |
||||||
|
var transient UProperty nextRef; |
||||||
|
var transient UProperty repOwner; |
||||||
|
|
||||||
|
var string commentString; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UPropertyCast extends Object; |
||||||
|
|
||||||
|
var UProperty nativeType; |
||||||
|
|
||||||
|
final function UProperty Cast(Property type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UState extends UStruct |
||||||
|
dependson(Unflect); |
||||||
|
|
||||||
|
var Unflect.Int64 probeMask; |
||||||
|
var Unflect.Int64 ignoreMask; |
||||||
|
var int stateFlags; |
||||||
|
var Unflect.Int16 labelTableOffset; |
||||||
|
var UField vfHash[256]; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UStateCast extends Object; |
||||||
|
|
||||||
|
var UState nativeType; |
||||||
|
|
||||||
|
final function UState Cast(State type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UStruct extends UField; |
||||||
|
|
||||||
|
var UTextBuffer scriptText; |
||||||
|
var UTextBuffer cppText; |
||||||
|
var UField children; |
||||||
|
var int propertiesSize; |
||||||
|
var name friendlyName; |
||||||
|
var array<byte> script; |
||||||
|
|
||||||
|
var int textPos; |
||||||
|
var int line; |
||||||
|
var struct EStructFlags { |
||||||
|
var bool native; |
||||||
|
var bool export; |
||||||
|
var bool long; |
||||||
|
var bool init; |
||||||
|
var bool unused1; |
||||||
|
var bool unused2; |
||||||
|
var bool unused3; |
||||||
|
var bool unused4; |
||||||
|
} StructFlags; |
||||||
|
|
||||||
|
var Property refLink; |
||||||
|
var Property propertyLink; |
||||||
|
var Property configLink; |
||||||
|
var Property constructorLink; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UStructCast extends Object; |
||||||
|
|
||||||
|
var UStruct nativeType; |
||||||
|
|
||||||
|
final function UStruct Cast(/*Core.Struct*/ Object type) { |
||||||
|
super(TypeCast).NativeCast(type); |
||||||
|
return nativeType; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 UTextBuffer extends Object; |
||||||
|
|
||||||
|
var private native const pointer outputDeviceVtbl; |
||||||
|
|
||||||
|
var int pos, top; |
||||||
|
var string text; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/** |
||||||
|
* One of the original Unflect files. |
||||||
|
* Copyright 2022-2023 EliotVU |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* 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 Unflect extends Object |
||||||
|
abstract; |
||||||
|
|
||||||
|
struct Int16 { |
||||||
|
var byte h, l; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Int64 { |
||||||
|
var int h, l; |
||||||
|
}; |
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
} |
@ -0,0 +1,409 @@ |
|||||||
|
/** |
||||||
|
* Config class for storing map lists. |
||||||
|
* Copyright 2020 bibibi |
||||||
|
* 2020-2023 Shtoyan |
||||||
|
* 2023 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class UnflectApi extends AcediaObject; |
||||||
|
|
||||||
|
//! This API offers advanced reflection capabilities for Unreal Script. |
||||||
|
//! |
||||||
|
//! Currently, the API supports the ability to replace the code of existing functions with |
||||||
|
//! custom code. |
||||||
|
//! This can greatly simplify the process of bug fixing and hooking into game events. |
||||||
|
|
||||||
|
/// A variable responsible for replacing function code in real-time. |
||||||
|
/// This variable is used to support dynamic function replacement/patching and event interception |
||||||
|
/// at runtime. |
||||||
|
var private UFunctionCast functionCaster; |
||||||
|
|
||||||
|
/// Maps lower case function name (specifies by the full path "package.class.functionName") |
||||||
|
/// to a `FunctionRule` that completely describes how it was replaced |
||||||
|
var private HashTable completedReplacements; |
||||||
|
/// Maps lower case function name (specifies by the full path "package.class.functionName") |
||||||
|
/// to the `ByteArrayBox` with that function's original code. |
||||||
|
var private HashTable originalScriptCodes; |
||||||
|
|
||||||
|
var private LoggerAPI.Definition warnSameFunction; |
||||||
|
var private LoggerAPI.Definition warnOverridingReplacement, errFailedToFindFunction; |
||||||
|
var private LoggerAPI.Definition errReplacementWithoutSources, errCannotCreateReplacementRule; |
||||||
|
|
||||||
|
protected function Constructor() { |
||||||
|
functionCaster = new class'UFunctionCast'; |
||||||
|
completedReplacements = _.collections.EmptyHashTable(); |
||||||
|
originalScriptCodes = _.collections.EmptyHashTable(); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() { |
||||||
|
_.memory.Free(completedReplacements); |
||||||
|
_.memory.Free(originalScriptCodes); |
||||||
|
completedReplacements = none; |
||||||
|
originalScriptCodes = none; |
||||||
|
functionCaster = none; |
||||||
|
} |
||||||
|
|
||||||
|
/// Reverts the replacement of the function's code, restoring its original behavior. |
||||||
|
/// |
||||||
|
/// The function to be reverted should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// It's worth noting that several function replacements do not stack. |
||||||
|
/// Even if [`ReplaceFunction()`] was called multiple times in a row to replace the same function, |
||||||
|
/// this method will cancel all the changes at once. |
||||||
|
/// |
||||||
|
/// This method returns true if the specified function was previously replaced and has now been |
||||||
|
/// successfully reverted. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// If the specified function cannot be found (but [`functionName`] isn't `none`), or if |
||||||
|
/// UnflectApi has not yet replaced it with any other function, this method will log an error. |
||||||
|
public final function bool RevertFunction(BaseText functionName) { |
||||||
|
local bool result; |
||||||
|
local FunctionReplacement storedReplacement; |
||||||
|
local ByteArrayBox storedSources; |
||||||
|
local Text functionNameLowerCase; |
||||||
|
local UFunction functionInstance; |
||||||
|
local SideEffect sideEffect; |
||||||
|
|
||||||
|
if (functionName == none) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
functionNameLowerCase = functionName.LowerCopy(); |
||||||
|
storedReplacement = FunctionReplacement(completedReplacements.GetItem(functionNameLowerCase)); |
||||||
|
if (storedReplacement != none) { |
||||||
|
storedSources = ByteArrayBox(originalScriptCodes.GetItem(functionNameLowerCase)); |
||||||
|
if (storedSources == none) { |
||||||
|
_.logger.Auto(errReplacementWithoutSources).Arg(functionNameLowerCase.Copy()); |
||||||
|
} else { |
||||||
|
functionInstance = FindFunction(functionNameLowerCase); |
||||||
|
if (functionInstance != none) { |
||||||
|
functionInstance.script = storedSources.Get(); |
||||||
|
completedReplacements.RemoveItem(functionNameLowerCase); |
||||||
|
sideEffect = storedReplacement.GetSideEffect(); |
||||||
|
_.sideEffects.RemoveInstance(sideEffect); |
||||||
|
result = true; |
||||||
|
} else { |
||||||
|
_.logger.Auto(errFailedToFindFunction).Arg(functionNameLowerCase.Copy()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
_.memory.Free4(storedReplacement, functionNameLowerCase, storedSources, sideEffect); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Reverts the replacement of the function's code, restoring its original behavior. |
||||||
|
/// |
||||||
|
/// The function to be reverted should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// It's worth noting that several function replacements do not stack. |
||||||
|
/// Even if [`ReplaceFunction()`] was called multiple times in a row to replace the same function, |
||||||
|
/// this method will cancel all the changes at once. |
||||||
|
/// |
||||||
|
/// This method returns true if the specified function was previously replaced and has now been |
||||||
|
/// successfully reverted. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// If the specified function cannot be found (but [`functionName`] isn't `none`), or if |
||||||
|
/// UnflectApi has not yet replaced it with any other function, this method will log an error. |
||||||
|
public final function bool RevertFunction_S(string functionName) { |
||||||
|
local bool result; |
||||||
|
local MutableText wrapper; |
||||||
|
|
||||||
|
wrapper = _.text.FromStringM(functionName); |
||||||
|
result = RevertFunction(wrapper); |
||||||
|
_.memory.Free(wrapper); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Determines whether the specified function has been replaced by UnflectApi. |
||||||
|
/// |
||||||
|
/// The function to be checked should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// If the function has been replaced, this method will return `true`; |
||||||
|
/// otherwise, it will return `false`. |
||||||
|
public final function bool IsFunctionReplaced(BaseText functionName) { |
||||||
|
local bool result; |
||||||
|
local Text functionNameLowerCase; |
||||||
|
|
||||||
|
if (functionName == none) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
functionNameLowerCase = functionName.LowerCopy(); |
||||||
|
result = completedReplacements.HasKey(functionNameLowerCase); |
||||||
|
_.memory.Free(functionNameLowerCase); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Determines whether the specified function has been replaced by UnflectApi. |
||||||
|
/// |
||||||
|
/// The function to be checked should be specified using its full path, in the format |
||||||
|
/// "package.class.functionName" (e.g. "KFMod.KFPawn.TossCash"). |
||||||
|
/// |
||||||
|
/// If the function has been replaced, this method will return `true`; |
||||||
|
/// otherwise, it will return `false`. |
||||||
|
public final function bool IsFunctionReplaced_S(string functionName) { |
||||||
|
local bool result; |
||||||
|
local MutableText wrapper; |
||||||
|
|
||||||
|
wrapper = _.text.FromStringM(functionName); |
||||||
|
result = IsFunctionReplaced(wrapper); |
||||||
|
_.memory.Free(wrapper); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Replaces one function with another by modifying its script code in real-time. |
||||||
|
/// |
||||||
|
/// The reason for replacement must be specified and will serve as a human-readable explanation |
||||||
|
/// for a [SideEffect] associated with the replacement. |
||||||
|
/// |
||||||
|
/// If you need to replace a function in a class, follow these steps: |
||||||
|
/// |
||||||
|
/// 1. Create a new class that extends the class in which the function you want to replace is |
||||||
|
/// located. |
||||||
|
/// 2. Declare that function in the created class. |
||||||
|
/// 3. **DO NOT** change the function declaration and argument types/amount. |
||||||
|
/// 4. **DO NOT** create new local variables, as this can cause random crashes. |
||||||
|
/// If you need additional variables, make them global and access them using the |
||||||
|
/// `class'myNewClass'.default.myNewVariable` syntax. |
||||||
|
/// 5. If you want to call or override parent code, make sure to always specify the desired parent |
||||||
|
/// class name. |
||||||
|
/// For example, use `super(TargetClass).PostBeginPlay()` instead of `super.PostBeginPlay()`. |
||||||
|
/// This will prevent runaway loop crashes. |
||||||
|
/// 6. Make your edits to the function's code, and then call the replacement function: |
||||||
|
/// ```unrealscript |
||||||
|
/// _.unflect.ReplaceFunction( |
||||||
|
/// "package.class.targetFunction", |
||||||
|
/// "myNewPackage.myNewClass.newFunction"); |
||||||
|
/// ``` |
||||||
|
/// |
||||||
|
/// Following these steps will help ensure that your code changes are compatible with the rest of |
||||||
|
/// the codebase and do not cause unexpected crashes. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// This method can log error messages in cases where: |
||||||
|
/// |
||||||
|
/// * The specified function(s) cannot be found. |
||||||
|
/// * An attempt is made to replace a function with itself. |
||||||
|
/// * An attempt is made to replace a function that has already been replaced. |
||||||
|
public final function bool ReplaceFunction( |
||||||
|
BaseText oldFunction, |
||||||
|
BaseText newFunction, |
||||||
|
BaseText replacementReason |
||||||
|
) { |
||||||
|
local bool result; |
||||||
|
local Text oldFunctionLowerCase, newFunctionLowerCase; |
||||||
|
|
||||||
|
if (oldFunction == none) return false; |
||||||
|
if (newFunction == none) return false; |
||||||
|
|
||||||
|
oldFunctionLowerCase = oldFunction.LowerCopy(); |
||||||
|
newFunctionLowerCase = newFunction.LowerCopy(); |
||||||
|
result = _replaceFunction(oldFunctionLowerCase, newFunctionLowerCase); |
||||||
|
if (result) { |
||||||
|
RecordNewReplacement(oldFunctionLowerCase, newFunctionLowerCase, replacementReason); |
||||||
|
} |
||||||
|
_.memory.Free2(oldFunctionLowerCase, newFunctionLowerCase); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/// Replaces one function with another by modifying its script code in real-time. |
||||||
|
/// |
||||||
|
/// The reason for replacement must be specified and will serve as a human-readable explanation |
||||||
|
/// for a [SideEffect] associated with the replacement. |
||||||
|
/// |
||||||
|
/// If you need to replace a function in a class, follow these steps: |
||||||
|
/// |
||||||
|
/// 1. Create a new class that extends the class in which the function you want to replace is |
||||||
|
/// located. |
||||||
|
/// 2. Declare that function in the created class. |
||||||
|
/// 3. **DO NOT** change the function declaration and argument types/amount. |
||||||
|
/// 4. **DO NOT** create new local variables, as this can cause random crashes. |
||||||
|
/// If you need additional variables, make them global and access them using the |
||||||
|
/// `class'myNewClass'.default.myNewVariable` syntax. |
||||||
|
/// 5. If you want to call or override parent code, make sure to always specify the desired parent |
||||||
|
/// class name. |
||||||
|
/// For example, use `super(TargetClass).PostBeginPlay()` instead of `super.PostBeginPlay()`. |
||||||
|
/// This will prevent runaway loop crashes. |
||||||
|
/// 6. Make your edits to the function's code, and then call the replacement function: |
||||||
|
/// ```unrealscript |
||||||
|
/// _.unflect.ReplaceFunction( |
||||||
|
/// "package.class.targetFunction", |
||||||
|
/// "myNewPackage.myNewClass.newFunction"); |
||||||
|
/// ``` |
||||||
|
/// |
||||||
|
/// Following these steps will help ensure that your code changes are compatible with the rest of |
||||||
|
/// the codebase and do not cause unexpected crashes. |
||||||
|
/// |
||||||
|
/// # Errors |
||||||
|
/// |
||||||
|
/// This method can log error messages in cases where: |
||||||
|
/// |
||||||
|
/// * The specified function(s) cannot be found. |
||||||
|
/// * An attempt is made to replace a function with itself. |
||||||
|
/// * An attempt is made to replace a function that has already been replaced. |
||||||
|
public final function bool ReplaceFunction_S( |
||||||
|
string oldFunction, |
||||||
|
string newFunction, |
||||||
|
string replacementReason |
||||||
|
) { |
||||||
|
local Text oldWrapper, newWrapper, reasonWrapper; |
||||||
|
local bool result; |
||||||
|
|
||||||
|
oldWrapper = _.text.FromString(oldFunction); |
||||||
|
newWrapper = _.text.FromString(newFunction); |
||||||
|
reasonWrapper = _.text.FromString(replacementReason); |
||||||
|
result = ReplaceFunction(oldWrapper, newWrapper, reasonWrapper); |
||||||
|
_.memory.Free3(oldWrapper, newWrapper, reasonWrapper); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
// Does actual work for function replacement. |
||||||
|
// Arguments are assumed to be not `none` and in lower case. |
||||||
|
private final function bool _replaceFunction(Text oldFunctionLowerCase, Text newFunctionLowerCase) { |
||||||
|
local ByteArrayBox initialCode; |
||||||
|
local UFunction replace, with; |
||||||
|
|
||||||
|
replace = FindFunction(oldFunctionLowerCase); |
||||||
|
if (replace == none) { |
||||||
|
_.logger.Auto(errFailedToFindFunction).Arg(oldFunctionLowerCase.Copy()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
with = FindFunction(newFunctionLowerCase); |
||||||
|
if (with == none) { |
||||||
|
_.logger.Auto(errFailedToFindFunction).Arg(newFunctionLowerCase.Copy()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (replace == with) { |
||||||
|
_.logger.Auto(warnSameFunction).Arg(oldFunctionLowerCase.Copy()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
// Remember old code, if haven't done so yet. |
||||||
|
// Since we attempt it on each replacement, the first recorded `script` value will be |
||||||
|
// the initial code. |
||||||
|
if (!originalScriptCodes.HasKey(oldFunctionLowerCase)) { |
||||||
|
initialCode = _.box.ByteArray(replace.script); |
||||||
|
originalScriptCodes.SetItem(oldFunctionLowerCase, initialCode); |
||||||
|
_.memory.Free(initialCode); |
||||||
|
} |
||||||
|
replace.script = with.script; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Arguments assumed to be not `none` and in lower case. |
||||||
|
private final function UFunction FindFunction(Text functionNameLowerCase) { |
||||||
|
local string stringName; |
||||||
|
|
||||||
|
stringName = functionNameLowerCase.ToString(); |
||||||
|
// We need to make sure functions are loaded before performing the replacement. |
||||||
|
DynamicLoadObject(GetClassName(stringName), class'class', true); |
||||||
|
return functionCaster.Cast(function(FindObject(stringName, class'Function'))); |
||||||
|
} |
||||||
|
|
||||||
|
// Arguments are assumed to be not `none`. |
||||||
|
// `oldFunctionLowerCase` and `newFunctionLowerCase` are assumed to be in lower case. |
||||||
|
private final function RecordNewReplacement( |
||||||
|
Text oldFunctionLowerCase, |
||||||
|
Text newFunctionLowerCase, |
||||||
|
BaseText replacementReason |
||||||
|
) { |
||||||
|
local SideEffect oldSideEffect, newSideEffect; |
||||||
|
local FunctionReplacement oldRule, newRule; |
||||||
|
|
||||||
|
// Remove old `FunctionReplacement`, if there is any |
||||||
|
oldRule = FunctionReplacement(completedReplacements.GetItem(oldFunctionLowerCase)); |
||||||
|
if (oldRule != none) { |
||||||
|
_.logger |
||||||
|
.Auto(warnOverridingReplacement) |
||||||
|
.Arg(oldFunctionLowerCase.Copy()) |
||||||
|
.Arg(oldRule.GetReplacerFunctionName()) |
||||||
|
.Arg(newFunctionLowerCase.Copy()); |
||||||
|
oldSideEffect = oldRule.GetSideEffect(); |
||||||
|
_.sideEffects.RemoveInstance(oldSideEffect); |
||||||
|
_.memory.Free2(oldRule, oldSideEffect); |
||||||
|
} |
||||||
|
// Create replacement instance |
||||||
|
newSideEffect = MakeSideEffect(oldFunctionLowerCase, newFunctionLowerCase, replacementReason); |
||||||
|
newRule = class'FunctionReplacement'.static |
||||||
|
.Make(oldFunctionLowerCase, newFunctionLowerCase, newSideEffect); |
||||||
|
completedReplacements.SetItem(oldFunctionLowerCase, newRule); |
||||||
|
if (newRule == none) { |
||||||
|
_.logger |
||||||
|
.Auto(errCannotCreateReplacementRule) |
||||||
|
.Arg(oldFunctionLowerCase.Copy()) |
||||||
|
.Arg(newFunctionLowerCase.Copy()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Arguments are assumed to be not `none`. |
||||||
|
// `oldFunctionLowerCase` and `newFunctionLowerCase` are assumed to be in lower case. |
||||||
|
private final function SideEffect MakeSideEffect( |
||||||
|
Text oldFunctionLowerCase, |
||||||
|
Text newFunctionLowerCase, |
||||||
|
BaseText replacementReason |
||||||
|
) { |
||||||
|
local SideEffect sideEffect; |
||||||
|
local MutableText status; |
||||||
|
|
||||||
|
// Add side effect |
||||||
|
status = oldFunctionLowerCase.MutableCopy(); |
||||||
|
status.Append(P(" -> ")); |
||||||
|
status.Append(newFunctionLowerCase); |
||||||
|
sideEffect = _.sideEffects.Add( |
||||||
|
P("Changed function's code"), |
||||||
|
replacementReason, |
||||||
|
P("AcediaCore"), |
||||||
|
P("UnflectAPI"), |
||||||
|
status |
||||||
|
); |
||||||
|
_.memory.Free(status); |
||||||
|
return sideEffect; |
||||||
|
} |
||||||
|
|
||||||
|
// Get the "package + dot + class" string for DynamicLoadObject() |
||||||
|
private final static function string GetClassName(string input) { |
||||||
|
local array<string> parts; |
||||||
|
|
||||||
|
// create an array |
||||||
|
Split(input, ".", parts); |
||||||
|
|
||||||
|
// state functions |
||||||
|
if (parts.length < 3) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
if (parts.length == 4) { |
||||||
|
ReplaceText(input, "." $ parts[2], ""); |
||||||
|
ReplaceText(input, "." $ parts[3], ""); |
||||||
|
} else { |
||||||
|
ReplaceText(input, "." $ parts[2], ""); |
||||||
|
} |
||||||
|
return input; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
defaultproperties { |
||||||
|
warnOverridingReplacement = (l=LOG_Error,m="Attempt to replace a function `%1` with function `%3` after it has already been replaced with `%2`.") |
||||||
|
warnSameFunction = (l=LOG_Error,m="Attempt to replace a function `%1` with itself.") |
||||||
|
errFailedToFindFunction = (l=LOG_Error,m="`UnflectApi` has failed to find function `%1`.") |
||||||
|
errReplacementWithoutSources = (l=LOG_Error,m="Cannot restore function `%1` - its initial source code wasn't preserved. This most likely means that it wasn't yet replaced.") |
||||||
|
errCannotCreateReplacementRule = (l=LOG_Error,m="`Cannot create new rule for replacing function `%1` with `%2` even though code was successfully replaces. This should happen, please report this.") |
||||||
|
} |
@ -1,115 +0,0 @@ |
|||||||
/** |
|
||||||
* Standard implementation for simple API for managing a list of |
|
||||||
* `SideEffect` info objects: can add, remove, return all and by package. |
|
||||||
* 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 KF1_SideEffectAPI extends SideEffectAPI; |
|
||||||
|
|
||||||
var private array<SideEffect> activeSideEffects; |
|
||||||
|
|
||||||
public function array<SideEffect> GetAll() |
|
||||||
{ |
|
||||||
local int i; |
|
||||||
|
|
||||||
for (i = 0; i < activeSideEffects.length; i += 1) { |
|
||||||
activeSideEffects[i].NewRef(); |
|
||||||
} |
|
||||||
return activeSideEffects; |
|
||||||
} |
|
||||||
|
|
||||||
public function SideEffect GetClass(class<SideEffect> sideEffectClass) |
|
||||||
{ |
|
||||||
local int i; |
|
||||||
|
|
||||||
if (sideEffectClass == none) { |
|
||||||
return none; |
|
||||||
} |
|
||||||
for (i = 0; i < activeSideEffects.length; i += 1) |
|
||||||
{ |
|
||||||
if (activeSideEffects[i].class == sideEffectClass) |
|
||||||
{ |
|
||||||
activeSideEffects[i].NewRef(); |
|
||||||
return activeSideEffects[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
return none; |
|
||||||
} |
|
||||||
|
|
||||||
public function array<SideEffect> GetFromPackage(BaseText packageName) |
|
||||||
{ |
|
||||||
local int i; |
|
||||||
local Text nextPackage; |
|
||||||
local array<SideEffect> result; |
|
||||||
|
|
||||||
if (packageName == none) { |
|
||||||
return result; |
|
||||||
} |
|
||||||
for (i = 0; i < activeSideEffects.length; i += 1) |
|
||||||
{ |
|
||||||
nextPackage = activeSideEffects[i].GetPackage(); |
|
||||||
if (nextPackage.Compare(packageName, SCASE_INSENSITIVE)) |
|
||||||
{ |
|
||||||
activeSideEffects[i].NewRef(); |
|
||||||
result[result.length] = activeSideEffects[i]; |
|
||||||
} |
|
||||||
_.memory.Free(nextPackage); |
|
||||||
} |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
public function bool Add(SideEffect newSideEffect) |
|
||||||
{ |
|
||||||
local int i; |
|
||||||
|
|
||||||
if (newSideEffect == none) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
for (i = 0; i < activeSideEffects.length; i += 1) |
|
||||||
{ |
|
||||||
if (activeSideEffects[i].class == newSideEffect.class) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
newSideEffect.NewRef(); |
|
||||||
activeSideEffects[activeSideEffects.length] = newSideEffect; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
public function bool RemoveClass( |
|
||||||
class<SideEffect> sideEffectClass) |
|
||||||
{ |
|
||||||
local int i; |
|
||||||
|
|
||||||
if (sideEffectClass == none) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
for (i = 0; i < activeSideEffects.length; i += 1) |
|
||||||
{ |
|
||||||
if (activeSideEffects[i].class == sideEffectClass) |
|
||||||
{ |
|
||||||
_.memory.Free(activeSideEffects[i]); |
|
||||||
activeSideEffects.Remove(i, 1); |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
defaultproperties |
|
||||||
{ |
|
||||||
} |
|
@ -1,65 +0,0 @@ |
|||||||
/** |
|
||||||
* Object representing a side effect introduced into the game/server. |
|
||||||
* Side effects in Acedia refer to changes that aren't a part of mod's main |
|
||||||
* functionality, but rather something necessary to make that functionality |
|
||||||
* possible that might also affect how other mods work. |
|
||||||
* This is a simple data container that is meant to describe relevant |
|
||||||
* changes to the human user. |
|
||||||
* 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 BroadcastSideEffect extends SideEffect |
|
||||||
dependson(BroadcastAPI); |
|
||||||
|
|
||||||
public final function Initialize(BroadcastAPI.InjectionLevel usedInjectionLevel) |
|
||||||
{ |
|
||||||
sideEffectName = |
|
||||||
_.text.FromString("AcediaCore's `BroadcastHandler` injected"); |
|
||||||
sideEffectDescription = |
|
||||||
_.text.FromString("Handling text and localized messages between server" |
|
||||||
@ "and clients requires AcediaCore to add its own `BroadcastHandler`" |
|
||||||
@ "into their linked list." |
|
||||||
@ "This is normal, since `BroadcastHandler` class was designed to allow" |
|
||||||
@ "mods to do that, however, for full functionality Acedia requires to" |
|
||||||
@ "inject it as the very first element (`BHIJ_Root` level injection)," |
|
||||||
@ "since some of the events become otherwise inaccessible." |
|
||||||
@ "This can result in incompatibility with other mods that are trying" |
|
||||||
@ "to do the same." |
|
||||||
@ "For that reason AcediaCore can also inject its `BroadcastHandler` as" |
|
||||||
@ "`BHIJ_Registered`."); |
|
||||||
sideEffectPackage = _.text.FromString("AcediaCore"); |
|
||||||
sideEffectSource = _.text.FromString("UnrealAPI"); |
|
||||||
if (usedInjectionLevel == BHIJ_Root) |
|
||||||
{ |
|
||||||
sideEffectStatus = |
|
||||||
_.text.FromFormattedString("{$TextPositive BHIJ_Root}"); |
|
||||||
} |
|
||||||
else if (usedInjectionLevel == BHIJ_Registered) |
|
||||||
{ |
|
||||||
sideEffectStatus = |
|
||||||
_.text.FromFormattedString("{$TextNetutral BHIJ_Registered}"); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
sideEffectStatus = |
|
||||||
_.text.FromFormattedString("{$TextNegative BHIJ_None (???)}"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
defaultproperties |
|
||||||
{ |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
/** |
|
||||||
* Object representing a side effect introduced into the game/server. |
|
||||||
* Side effects in Acedia refer to changes that aren't a part of mod's main |
|
||||||
* functionality, but rather something necessary to make that functionality |
|
||||||
* possible that might also affect how other mods work. |
|
||||||
* This is a simple data container that is meant to describe relevant |
|
||||||
* changes to the human user. |
|
||||||
* 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 GameRulesSideEffect extends SideEffect; |
|
||||||
|
|
||||||
public final function Initialize() |
|
||||||
{ |
|
||||||
sideEffectName = |
|
||||||
_.text.FromString("AcediaCore's `AcediaGameRules` added"); |
|
||||||
sideEffectDescription = |
|
||||||
_.text.FromString("`GameRule`s is one of the main ways to get notified" |
|
||||||
@ "about various gameplay-related events in Unreal Engine." |
|
||||||
@ "Of course AcediaCore would require handling some of those events," |
|
||||||
@ "depending on how it's used."); |
|
||||||
sideEffectPackage = _.text.FromString("AcediaCore"); |
|
||||||
sideEffectSource = _.text.FromString("UnrealAPI"); |
|
||||||
sideEffectStatus = _.text.FromFormattedString("{$TextPositive active}"); |
|
||||||
} |
|
||||||
|
|
||||||
defaultproperties |
|
||||||
{ |
|
||||||
} |
|
@ -1,164 +0,0 @@ |
|||||||
/** |
|
||||||
* Object representing a side effect introduced into the game/server. |
|
||||||
* Side effects in Acedia refer to changes that aren't a part of mod's main |
|
||||||
* functionality, but rather something necessary to make that functionality |
|
||||||
* possible that might also affect how other mods work. |
|
||||||
* This is a simple data container that is meant to describe relevant |
|
||||||
* changes to the human user. |
|
||||||
* 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 SideEffect extends AcediaObject |
|
||||||
abstract; |
|
||||||
|
|
||||||
/** |
|
||||||
* # Side effects |
|
||||||
* |
|
||||||
* In Acedia "side effect" refers to changes that aren't a part of mod's |
|
||||||
* main functionality, but rather something necessary to make that |
|
||||||
* functionality possible that might also affect how other mods work. Their |
|
||||||
* purpose is to help developers and server admins figure out what changes were |
|
||||||
* performed by Acedia or mods based on it. |
|
||||||
* What needs to be considered a side effect is loosely defined and they |
|
||||||
* are simply a tool to inform others that something they might not have |
|
||||||
* expected has happened, that can possibly break other (their) mods. |
|
||||||
* AcediaCore, for example, tried to leave a minimal footprint, avoiding |
|
||||||
* making any changes to the game classes unless requested, but it still has to |
|
||||||
* do some changes (adding `GameRules`, replacing damage types for some zeds, |
|
||||||
* etc.) and `SideEffect`s can be used to document these changes. They can be |
|
||||||
* used in a similar way for AcediaFixes - a package that is only meant for |
|
||||||
* fixing bugs, but inevitably has to make a lot of under the hood changes to |
|
||||||
* achieve that. |
|
||||||
* On the other hand gameplay mods like Futility can make a lot of changes, |
|
||||||
* but they can all be just expected part of its direct functionality: we |
|
||||||
* expect feature that shares dosh of leavers to alter players' dosh values, so |
|
||||||
* this is not a side effect. Such mods are likely not going to have to specify |
|
||||||
* any side effects whatsoever. |
|
||||||
* |
|
||||||
* ## Implementing your own `SideEffect`s |
|
||||||
* |
|
||||||
* Simply make a non-abstract child class based on `SideEffect`, create its |
|
||||||
* instance filled with necessary data and register it in `SideEffectAPI` once |
|
||||||
* side effect is introduced. If you revert introduced side effect, you should |
|
||||||
* remove registered object through the same API. |
|
||||||
* Each class of the `SideEffect` is supposed to represent a particular |
|
||||||
* side effect introduced into the game engine. Whether side effect is active |
|
||||||
* is decided by whether it is currently registered in `SideEffectAPI`. |
|
||||||
* |
|
||||||
* NOTE: `SideEffect` should not have any logic and should serve as |
|
||||||
* an immutable data container. |
|
||||||
*/ |
|
||||||
|
|
||||||
var protected Text sideEffectName; |
|
||||||
var protected Text sideEffectDescription; |
|
||||||
var protected Text sideEffectPackage; |
|
||||||
var protected Text sideEffectSource; |
|
||||||
var protected Text sideEffectStatus; |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns name (short description) of the caller `SideEffect`. User need to |
|
||||||
* be able to tell what this `SideEffect` is generally about from the glance at |
|
||||||
* this name. |
|
||||||
* |
|
||||||
* Guideline is for this value to not exceed `80` characters, but this is not |
|
||||||
* enforced. |
|
||||||
* |
|
||||||
* Must not be `none`. |
|
||||||
* |
|
||||||
* @return Name (short description) of the caller `SideEffect`. |
|
||||||
* Guaranteed to not be `none`. |
|
||||||
*/ |
|
||||||
public function Text GetName() |
|
||||||
{ |
|
||||||
if (sideEffectName != none) { |
|
||||||
return sideEffectName.Copy(); |
|
||||||
} |
|
||||||
return none; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns description of the caller `SideEffect`. This should describe what |
|
||||||
* was done and why relevant change was necessary. |
|
||||||
* |
|
||||||
* Must not be `none`. |
|
||||||
* |
|
||||||
* @return Description of the caller `SideEffect`. |
|
||||||
* Guaranteed to not be `none`. |
|
||||||
*/ |
|
||||||
public function Text GetDescription() |
|
||||||
{ |
|
||||||
if (sideEffectDescription != none) { |
|
||||||
return sideEffectDescription.Copy(); |
|
||||||
} |
|
||||||
return none; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns name of the package ("*.u" file) that introduced this change. |
|
||||||
* |
|
||||||
* Note that if package "A" actually performed the change because another |
|
||||||
* package "B" requested certain functionality, it is still package "A" that is |
|
||||||
* responsible for the side effect. |
|
||||||
* |
|
||||||
* Must not be `none`. |
|
||||||
* |
|
||||||
* @return Name of the package ("*.u" file) that introduced this change. |
|
||||||
* Guaranteed to not be `none`. |
|
||||||
*/ |
|
||||||
public function Text GetPackage() |
|
||||||
{ |
|
||||||
if (sideEffectPackage != none) { |
|
||||||
return sideEffectPackage.Copy(); |
|
||||||
} |
|
||||||
return none; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* What part of package caused this change. For huge packages can be used to |
|
||||||
* further specify what introduced the change. |
|
||||||
* |
|
||||||
* Returned value can be `none` (e.g. when it is unnecessary for small |
|
||||||
* packages). |
|
||||||
* |
|
||||||
* @return Name (short description) of the part of the package that caused |
|
||||||
* caller `SideEffect`. |
|
||||||
*/ |
|
||||||
public function Text GetSource() |
|
||||||
{ |
|
||||||
if (sideEffectSource != none) { |
|
||||||
return sideEffectSource.Copy(); |
|
||||||
} |
|
||||||
return none; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Status of the caller `SideEffect`. Some side effects can be introduced in |
|
||||||
* several different ways - this value needs to help distinguish between them. |
|
||||||
* |
|
||||||
* @return Status of the caller `SideEffect`. |
|
||||||
*/ |
|
||||||
public function Text GetStatus() |
|
||||||
{ |
|
||||||
if (sideEffectStatus != none) { |
|
||||||
return sideEffectStatus.Copy(); |
|
||||||
} |
|
||||||
return none; |
|
||||||
} |
|
||||||
|
|
||||||
defaultproperties |
|
||||||
{ |
|
||||||
} |
|
@ -1,76 +0,0 @@ |
|||||||
/** |
|
||||||
* Base class for simple API for managing a list of `SideEffect` info |
|
||||||
* objects: can add, remove, return all and by package. |
|
||||||
* 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 SideEffectAPI extends AcediaObject |
|
||||||
abstract; |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns all so far registered `SideEffect`s. |
|
||||||
* |
|
||||||
* @return Array of all registered `SideEffect`s. |
|
||||||
*/ |
|
||||||
public function array<SideEffect> GetAll(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns active `SideEffect` instance of the specified class |
|
||||||
* `sideEffectClass`. |
|
||||||
* |
|
||||||
* @param sideEffectClass Class of side effect to return active instance of. |
|
||||||
* @return Active `SideEffect` instance of the specified class |
|
||||||
* `sideEffectClass`. |
|
||||||
* `none` if either `sideEffectClass` is `none` or side effect of such |
|
||||||
* class is not currently active. |
|
||||||
*/ |
|
||||||
public function SideEffect GetClass(class<SideEffect> sideEffectClass); |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns all so far registered `SideEffect`s from a package `packageName` |
|
||||||
* (case insensitive). |
|
||||||
* |
|
||||||
* @param packageName Name of the package, in `SideEffect`s from which we are |
|
||||||
* interested. Must be not `none`. |
|
||||||
* @return Array of all registered `SideEffect`s from a package `packageName`. |
|
||||||
* If `none`, returns an empty array. |
|
||||||
*/ |
|
||||||
public function array<SideEffect> GetFromPackage(BaseText packageName); |
|
||||||
|
|
||||||
/** |
|
||||||
* Registers a new `SideEffect` object as active. |
|
||||||
* |
|
||||||
* @param newSideEffect Instance of some `SideEffect` class to register as |
|
||||||
* active side effect. Must not be `none`. |
|
||||||
* @return `true` if new side effect was added and `false` otherwise. |
|
||||||
*/ |
|
||||||
public function bool Add(SideEffect newSideEffect); |
|
||||||
|
|
||||||
/** |
|
||||||
* Removes `SideEffect` of the specified sub-class from the list of active |
|
||||||
* side effects. |
|
||||||
* |
|
||||||
* @param sideEffectClass Class of the side effect to remove. |
|
||||||
* @return `true` if some side effect was removed as a result of this operation |
|
||||||
* and `false` otherwise (even if there was no side effect of specified |
|
||||||
* class to begin with). |
|
||||||
*/ |
|
||||||
public function bool RemoveClass(class<SideEffect> sideEffectClass); |
|
||||||
|
|
||||||
defaultproperties |
|
||||||
{ |
|
||||||
} |
|
Loading…
Reference in new issue