From 71248cb1c4f59202af6028c57cb3ca805a1ecaae Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Thu, 14 Jul 2022 03:19:31 +0700 Subject: [PATCH] Add `SideEffect`s class --- sources/ClientRealm/ClientGlobal.uc | 8 +- .../CoreRealm/API/SideEffects/SideEffect.uc | 164 ++++++++++++++++++ .../API/SideEffects/SideEffectAPI.uc | 155 +++++++++++++++++ sources/CoreRealm/CoreGlobal.uc | 36 ++++ sources/ServerRealm/ServerGlobal.uc | 3 +- 5 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 sources/CoreRealm/API/SideEffects/SideEffect.uc create mode 100644 sources/CoreRealm/API/SideEffects/SideEffectAPI.uc create mode 100644 sources/CoreRealm/CoreGlobal.uc diff --git a/sources/ClientRealm/ClientGlobal.uc b/sources/ClientRealm/ClientGlobal.uc index e4a00ab..84236bd 100644 --- a/sources/ClientRealm/ClientGlobal.uc +++ b/sources/ClientRealm/ClientGlobal.uc @@ -19,8 +19,9 @@ * You should have received a copy of the GNU General Public License * along with Acedia. If not, see . */ -class ClientGlobal extends Object; +class ClientGlobal extends CoreGlobal; +var protected bool initialized; // `Global` is expected to behave like a singleton and will store it's // main instance in this variable's default value. var protected ClientGlobal myself; @@ -38,6 +39,11 @@ public final static function ClientGlobal GetInstance() protected function Initialize() { + if (initialized) { + return; + } + super.Initialize(); + initialized = true; } defaultproperties diff --git a/sources/CoreRealm/API/SideEffects/SideEffect.uc b/sources/CoreRealm/API/SideEffects/SideEffect.uc new file mode 100644 index 0000000..7b95877 --- /dev/null +++ b/sources/CoreRealm/API/SideEffects/SideEffect.uc @@ -0,0 +1,164 @@ +/** + * 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 . + */ +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 footpring, 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. + * + * ## Implemention 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` cahracters, 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 +{ +} \ No newline at end of file diff --git a/sources/CoreRealm/API/SideEffects/SideEffectAPI.uc b/sources/CoreRealm/API/SideEffects/SideEffectAPI.uc new file mode 100644 index 0000000..396fd5d --- /dev/null +++ b/sources/CoreRealm/API/SideEffects/SideEffectAPI.uc @@ -0,0 +1,155 @@ +/** + * 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 . + */ +class SideEffectAPI extends AcediaObject; + +var private array activeSideEffects; + +/** + * Returns all so far registered `SideEffect`s. + * + * @return Array of all registered `SideEffect`s. + */ +public final function array GetAll() +{ + local int i; + + for (i = 0; i < activeSideEffects.length; i += 1) { + activeSideEffects[i].NewRef(); + } + return activeSideEffects; +} + +/** + * 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 final function SideEffect GetClass(class 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; +} + +/** + * Returns all so far registered `SideEffect`s from a package `packageName` + * (case insensitive). + * + * @param packageName Name opf 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 final function array GetFromPackage(BaseText packageName) +{ + local int i; + local Text nextPackage; + local array 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; +} + +/** + * 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 final function bool AddSideEffect(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; +} + +/** + * 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` otherise (even if there was no side effect of specified + * class to begin with). + */ +public final function bool RemoveSideEffectClass( + class 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 +{ +} \ No newline at end of file diff --git a/sources/CoreRealm/CoreGlobal.uc b/sources/CoreRealm/CoreGlobal.uc new file mode 100644 index 0000000..ffc8709 --- /dev/null +++ b/sources/CoreRealm/CoreGlobal.uc @@ -0,0 +1,36 @@ +/** + * Base class for objects that will provide an access to a Acedia's client- and + * server-specific functionality by giving a reference to this object to all + * Acedia's objects and actors, emulating a global API namespace. + * Copyright 2022 Anton Tarasenko + *------------------------------------------------------------------------------ + * This file is part of Acedia. + * + * Acedia is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * Acedia is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Acedia. If not, see . + */ +class CoreGlobal extends Object; + +var public SideEffectAPI sideEffects; + +protected function Initialize() +{ + local MemoryAPI api; + + api = class'Global'.static.GetInstance().memory; + sideEffects = SideEffectAPI(api.Allocate(class'SideEffectAPI')); +} + +defaultproperties +{ +} \ No newline at end of file diff --git a/sources/ServerRealm/ServerGlobal.uc b/sources/ServerRealm/ServerGlobal.uc index 0bdd8cc..3cce56e 100644 --- a/sources/ServerRealm/ServerGlobal.uc +++ b/sources/ServerRealm/ServerGlobal.uc @@ -19,7 +19,7 @@ * You should have received a copy of the GNU General Public License * along with Acedia. If not, see . */ -class ServerGlobal extends Object; +class ServerGlobal extends CoreGlobal; var protected bool initialized; // `Global` is expected to behave like a singleton and will store it's @@ -46,6 +46,7 @@ protected function Initialize() if (initialized) { return; } + super.Initialize(); _ = class'Global'.static.GetInstance(); kf = KFFrontend(_.memory.Allocate(class'KF1_Frontend')); // TODO: this is hack, needs to be redone later