Browse Source

Add `SideEffects` settings

Previously settings about side effects were scattered through different
relevant classes.
Now they will all be recorded in a single config object.
Additionally, log messages about both success and failure of introducing
these side effects.
pull/8/head
Anton Tarasenko 2 years ago
parent
commit
fa5efe3537
  1. 49
      config/AcediaSystem.ini
  2. 92
      sources/CoreRealm/SideEffects.uc
  3. 56
      sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc
  4. 24
      sources/Unreal/BroadcastsAPI/BroadcastAPI.uc
  5. 6
      sources/Unreal/BroadcastsAPI/BroadcastEventsObserver.uc
  6. 19
      sources/Unreal/GameRulesAPI/GameRulesAPI.uc

49
config/AcediaSystem.ini

@ -1,23 +1,16 @@
; Every single option in this config should be considered [ADVANCED]
[AcediaCore.BroadcastEventsObserver]
; Acedia requires injecting it's own `BroadcastHandler` to listen to
; the broadcasted messages.
; It's normal for a mod to add it's own broadcast handler: broadcast handlers
; are implemented in such a way that they form a linked list and, after
; first (root) handler receives a message it tells about said message to
; the next handler, which does the same, propagating messages through
; Every single option in this config should be considered [ADVANCED].
; DO NOT CHANGE THEM unless you are sure you know what you're doing.
[AcediaCore.SideEffects]
; Acedia requires adding its own `GameRules` to listen to many different
; game events.
; It's normal for a mod to add its own game rules: game rules are
; implemented in such a way that they form a linked list and, after
; first (root) rules object receives a message it tells about said message to
; the next rules object, which does the same, propagating messages through
; the whole list.
; If you do not wish Acedia to add it's own handler, you should specify `0` as
; `usedInjectionLevel`'s value. If you want to allow it to simply add it's
; broadcast handler to the end of the handler's linked list, as described above,
; set it to `1`.
; However, more information can be obtained if Acedia's broadcast handler is
; inserted at the root of the whole chain. This is the prefered way for Acedia
; and if you do not have a reason to forbid it, you should leave this value
; at `2`.
usedInjectionLevel=BHIJ_Root
[AcediaCore.KF1_HealthComponent]
; This is the least offensive side effect of AcediaCore and there should
; be no reason to prevents its `GameRules` from being added.
allowAddingGameRules=true
; Unfortunately, thanks to the TWI's code, there's no way to catch events
; of when certain kinds of damage are dealt: from welder, bloat's bile and
; siren's scream. At least not without something drastic, like replacing game
@ -42,7 +35,23 @@ usedInjectionLevel=BHIJ_Root
; 3. A lot of siren's visual damage effects code does direct checks for
; `SirenScreamDamage` class. These can also break, stopping working as
; intended.
replaceBloatAndSirenDamageTypes=true
allowReplacingDamageTypes=true
; Acedia requires injecting its own `BroadcastHandler` to listen to
; the broadcasted messages.
; It's normal for a mod to add its own broadcast handler: broadcast handlers
; are implemented in such a way that they form a linked list and, after
; first (root) handler receives a message it tells about said message to
; the next handler, which does the same, propagating messages through
; the whole list.
; If you do not wish Acedia to add its own handler, you should specify
; `BHIJ_None` as `broadcastHandlerInjectionLevel`'s value. If you want to allow
; it to simply add its broadcast handler to the end of the handler's
; linked list, as described above, set it to `BHIJ_Registered`.
; However, more information can be obtained if Acedia's broadcast handler is
; inserted at the root of the whole chain. This is the preferred way for
; Acedia and if you do not have a reason to forbid that (for example, for mod
; compatibility reasons), you should set this value at `BHIJ_Root`.
broadcastHandlerInjectionLevel=BHIJ_Root
[AcediaCore.UserAPI]
userDataDBLink="local:database/users"

92
sources/CoreRealm/SideEffects.uc

@ -0,0 +1,92 @@
/**
* This object is meant purely as a dummy class to load config values about
* side effects in AcediaCore. Class name is chosen to make config more
* readable.
* 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 SideEffects extends AcediaObject
dependson(BroadcastAPI)
abstract
config(AcediaSystem);
/**
* Acedia requires adding its own `GameRules` to listen to many different
* game events.
* It's normal for a mod to add its own game rules: game rules are
* implemented in such a way that they form a linked list and, after
* first (root) rules object receives a message it tells about said message to
* the next rules object, which does the same, propagating messages through
* the whole list.
* This is the least offensive side effect of AcediaCore and there should
* be no reason to prevents its `GameRules` from being added.
*/
var public const config bool allowAddingGameRules;
/**
* Unfortunately, thanks to the TWI's code, there's no way to catch events
* of when certain kinds of damage are dealt: from welder, bloat's bile and
* siren's scream. At least not without something drastic, like replacing game
* type class.
* As a workaround, Acedia can optionally replace bloat and siren damage
* type to at least catch damage dealt by zeds (as being dealt welder damage is
* pretty rare and insignificant). This change has several unfortunate
* side-effects:
* 1. Potentially breaking mods that are looking for `DamTypeVomit` and
* `SirenScreamDamage` damage types specifically. Fixing this issue
* would require these mods to either also try and catch Acedia's
* replacements `AcediaCore.Dummy_DamTypeVomit` and
* `AcediaCore.Dummy_SirenScreamDamage` or to catch any child classes
* of `DamTypeVomit` and `SirenScreamDamage` (as Acedia's replacements
* are also their child classes).
* 2. Breaking some achievements that rely on
* `KFSteamStatsAndAchievements`'s `KilledEnemyWithBloatAcid()` method
* being called. This is mostly dealt with by Acedia calling it
* manually. However it relies on killed pawn to have
* `lastDamagedByType` set to `DamTypeVomit`, which sometimes might not
* be the case. Achievements should still be obtainable.
* 3. A lot of siren's visual damage effects code does direct checks for
* `SirenScreamDamage` class. These can also break, stopping working as
* intended.
*/
var public const config bool allowReplacingDamageTypes;
/**
* Acedia requires injecting its own `BroadcastHandler` to listen to
* the broadcasted messages.
* It's normal for a mod to add its own broadcast handler: broadcast
* handlers are implemented in such a way that they form a linked list and,
* after first (root) handler receives a message it tells about said message to
* the next handler, which does the same, propagating messages through
* the whole list.
* If you do not wish Acedia to add its own handler, you should specify
* `BHIJ_None` as `broadcastHandlerInjectionLevel`'s value. If you want to
* allow it to simply add its broadcast handler to the end of the handler's
* linked list, as described above, set it to `BHIJ_Registered`.
* However, more information can be obtained if Acedia's broadcast handler
* is inserted at the root of the whole chain. This is the preferred way for
* Acedia and if you do not have a reason to forbid that (for example, for mod
* compatibility reasons), you should set this value at `BHIJ_Root`.
*/
var public const config BroadcastAPI.InjectionLevel broadcastHandlerInjectionLevel;
defaultproperties
{
allowAddingGameRules = true
allowReplacingDamageTypes = true
broadcastHandlerInjectionLevel = BHIJ_Root
}

56
sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc

@ -22,40 +22,13 @@ class KF1_HealthComponent extends AHealthComponent
config(AcediaSystem);
var private bool connectedToGameRules;
var private bool replacedDamageTypes;
/**
* Unfortunately, thanks to the TWI's code, there's no way to catch events
* of when certain kinds of damage are dealt: from welder, bloat's bile and
* siren's scream. At least not without something drastic, like replacing game
* type class.
* As a workaround, Acedia can optionally replace bloat and siren damage
* type to at least catch damage dealt by zeds (as being dealt welder damage is
* pretty rare and insignificant). This change has several unfortunate
* side-effects:
* 1. Potentially breaking mods that are looking for `DamTypeVomit` and
* `SirenScreamDamage` damage types specifically. Fixing this issue
* would require these mods to either also try and catch Acedia's
* replacements `AcediaCore.Dummy_DamTypeVomit` and
* `AcediaCore.Dummy_SirenScreamDamage` or to catch any child classes
* of `DamTypeVomit` and `SirenScreamDamage` (as Acedia's replacements
* are also their child classes).
* 2. Breaking some achievements that rely on
* `KFSteamStatsAndAchievements`'s `KilledEnemyWithBloatAcid()` method
* being called. This is mostly dealt with by Acedia calling it
* manually. However it relies on killed pawn to have
* `lastDamagedByType` set to `DamTypeVomit`, which sometimes might not
* be the case. Achievements should still be obtainable.
* 3. A lot of siren's visual damage effects code does direct checks for
* `SirenScreamDamage` class. These can also break, stopping working as
* intended.
*/
var private const config bool replaceBloatAndSirenDamageTypes;
var private bool triedToReplaceDamageTypes;
var private const int TDAMAGE, TORIGINAL_DAMAGE, THIT_LOCATION, TMOMENTUM;
var private LoggerAPI.Definition infoReplacingDamageTypes, errNoServerLevelCore;
var private LoggerAPI.Definition infoRestoringReplacingDamageTypes;
var private LoggerAPI.Definition warnReplacingDamageTypesForbidden;
protected function Finalizer()
{
@ -66,11 +39,6 @@ protected function Finalizer()
_.unreal.gameRules.OnScoreKill(self).Disconnect();
connectedToGameRules = false;
}
if (replaceBloatAndSirenDamageTypes)
{
RestoreDamageTypes();
replacedDamageTypes = false;
}
}
public function Health_OnDamage_Slot OnDamage(AcediaObject receiver)
@ -95,10 +63,15 @@ private final function TryReplaceDamageTypes()
{
local LevelCore core;
if (!replaceBloatAndSirenDamageTypes) return;
if (replacedDamageTypes) return;
replacedDamageTypes = true;
if (triedToReplaceDamageTypes) {
return;
}
triedToReplaceDamageTypes = true;
if (!class'SideEffects'.default.allowReplacingDamageTypes)
{
_.logger.Auto(warnReplacingDamageTypesForbidden);
return;
}
_.logger.Auto(infoReplacingDamageTypes);
core = class'ServerLevelCore'.static.GetInstance();
if (core != none)
@ -153,7 +126,6 @@ private final function RestoreDamageTypes()
class'ZombieSiren_XMas'.default.screamDamageType = class'SirenScreamDamage';
class'ZombieSiren_CIRCUS'.default.screamDamageType =
class'SirenScreamDamage';
replacedDamageTypes = false;
}
private function int OnNetDamageHandler(
@ -248,7 +220,6 @@ private function UpdateBileAchievement(Controller killer, Controller killed)
defaultproperties
{
replaceBloatAndSirenDamageTypes = true
TDAMAGE = 0
stringConstants(0) = "damage"
TORIGINAL_DAMAGE = 1
@ -257,7 +228,8 @@ defaultproperties
stringConstants(2) = "hitLocation"
TMOMENTUM = 3
stringConstants(3) = "momentum"
infoReplacingDamageTypes = (l=LOG_Info,m="Replacing bloat's and siren's damage types with dummy ones.")
infoRestoringReplacingDamageTypes = (l=LOG_Info,m="Restoring bloat and siren's damage types to their original values.")
infoReplacingDamageTypes = (l=LOG_Info,m="Replaced bloat's and siren's damage types with dummy ones.")
infoRestoringReplacingDamageTypes = (l=LOG_Info,m="Restored bloat and siren's damage types to their original values.")
errNoServerLevelCore = (l=LOG_Error,m="Server level core is missing. Either this isn't a server or Acedia was wrongly initialized. Bloat and siren damage type will not be replaced.")
warnReplacingDamageTypesForbidden = (l=LOG_Warning,m="`OnDamage()` signal is used, but might not work properly because bloat and siren damage type replacement is forbidden by AcediaCore's settings: in file \"AcediaSystem.ini\", section [AcediaCore.SideEffects], variable `allowReplacingDamageTypes`.")
}

24
sources/Unreal/BroadcastsAPI/BroadcastAPI.uc

@ -20,6 +20,10 @@
*/
class BroadcastAPI extends AcediaObject;
// Tracks if we have already tried to add our own `BroadcastHandler` to avoid
// wasting resources/spamming errors in the log about our inability to do so
var private bool triedToInjectBroadcastHandler;
/**
* Defines ways to add a new `BroadcastHandler` into the `GameInfo`'s
* `BroadcastHandler` linked list.
@ -58,6 +62,8 @@ struct LocalizedMessage
};
var private LoggerAPI.Definition infoInjectedBroadcastEventsObserver;
var private LoggerAPI.Definition errBroadcasthandlerForbidden;
var private LoggerAPI.Definition errBroadcasthandlerUnknown;
/**
* Called before text message is sent to any player, during the check for
@ -277,10 +283,11 @@ private final function TryInjectBroadcastHandler(UnrealService service)
local BroadcastSideEffect sideEffect;
local BroadcastEventsObserver broadcastObserver;
if (IsAdded(class'BroadcastEventsObserver')) {
if (triedToInjectBroadcasthandler) {
return;
}
usedLevel = class'BroadcastEventsObserver'.default.usedInjectionLevel;
triedToInjectBroadcasthandler = true;
usedLevel = class'SideEffects'.default.broadcastHandlerInjectionLevel;
broadcastObserver = BroadcastEventsObserver(_.unreal.broadcasts.Add(
class'BroadcastEventsObserver', usedLevel));
if (broadcastObserver != none)
@ -294,6 +301,17 @@ private final function TryInjectBroadcastHandler(UnrealService service)
_.logger
.Auto(infoInjectedBroadcastEventsObserver)
.Arg(InjectionLevelIntoText(usedLevel));
return;
}
// We are here if we have failed
if (usedLevel == BHIJ_None) {
_.logger.Auto(errBroadcastHandlerForbidden);
}
else
{
_.logger
.Auto(errBroadcastHandlerUnknown)
.Arg(InjectionLevelIntoText(usedLevel));
}
}
@ -448,4 +466,6 @@ public final function bool IsAdded(class<BroadcastHandler> BHClassToFind)
defaultproperties
{
infoInjectedBroadcastEventsObserver = (l=LOG_Info,m="Injected AcediaCore's `BroadcastEventsObserver` with level `%1`.")
errBroadcastHandlerForbidden = (l=LOG_Error,m="Injected AcediaCore's `BroadcastEventsObserver` is required, but forbidden by AcediaCore's settings: in file \"AcediaSystem.ini\", section [AcediaCore.SideEffects], variable `broadcastHandlerInjectionLevel`.")
errBroadcastHandlerUnknown = (l=LOG_Error,m="Injected AcediaCore's `BroadcastEventsObserver` failed to be injected with level `%1` for unknown reason.")
}

6
sources/Unreal/BroadcastsAPI/BroadcastEventsObserver.uc

@ -30,7 +30,7 @@ class BroadcastEventsObserver extends Engine.BroadcastHandler
// To alleviate this issue Acedia allows server admins to control how it's
// `BroadcastHandler` is injected. Do note however that anything other than
// `BHIJ_Root` can lead to issues with Acedia's features.
var public config const BroadcastAPI.InjectionLevel usedInjectionLevel;
var private BroadcastAPI.InjectionLevel usedInjectionLevel;
/**
* To understand how what our broadcast handler does, let us first explain
@ -206,6 +206,8 @@ var private Broadcast_OnHandleTextFor_Signal onHandleTextFor;
public final function Initialize(UnrealService service)
{
usedInjectionLevel =
class'SideEffects'.default.broadcastHandlerInjectionLevel;
if (usedInjectionLevel != BHIJ_Root) {
Disable('Tick');
}
@ -475,9 +477,7 @@ event Tick(float delta)
ResetTracking();
}
// senders, out for handletext
defaultproperties
{
blockAllowsBroadcast = false
usedInjectionLevel = BHIJ_Root
}

19
sources/Unreal/GameRulesAPI/GameRulesAPI.uc

@ -20,7 +20,13 @@
*/
class GameRulesAPI extends AcediaObject;
// Tracks if we have already tried to add our own `BroadcastHandler` to avoid
// wasting resources/spamming errors in the log about our inability to do so
var private bool triedToInjectGameRules;
var private LoggerAPI.Definition infoAddedGameRules;
var private LoggerAPI.Definition errGameRulesForbidden;
var private LoggerAPI.Definition errGameRulesUnknown;
/**
* Called when game decides on a player's spawn point. If a `NavigationPoint`
@ -272,7 +278,13 @@ private final function TryAddingGameRules(UnrealService service)
local AcediaGameRules gameRules;
local GameRulesSideEffect sideEffect;
if (AreAdded(class'AcediaGameRules')) {
if (triedToInjectGameRules) {
return;
}
triedToInjectGameRules = true;
if (!class'SideEffects'.default.allowAddingGameRules)
{
_.logger.Auto(errGameRulesForbidden);
return;
}
gameRules = AcediaGameRules(Add(class'AcediaGameRules'));
@ -286,6 +298,9 @@ private final function TryAddingGameRules(UnrealService service)
_.memory.Free(sideEffect);
_.logger.Auto(infoAddedGameRules);
}
else {
_.logger.Auto(errGameRulesUnknown);
}
}
/**
@ -391,4 +406,6 @@ public final function bool AreAdded(
defaultproperties
{
infoAddedGameRules = (l=LOG_Info,m="Added AcediaCore's `AcediaGameRules`.")
errGameRulesForbidden = (l=LOG_Error,m="Adding AcediaCore's `AcediaGameRules` is required, but forbidden by AcediaCore's settings: in file \"AcediaSystem.ini\", section [AcediaCore.SideEffects], variable `allowAddingGameRules`.")
errGameRulesUnknown = (l=LOG_Error,m="Adding AcediaCore's `AcediaGameRules` failed to be injected with level for unknown reason.")
}
Loading…
Cancel
Save