diff --git a/config/AcediaSystem.ini b/config/AcediaSystem.ini
index 980a049..fdc0466 100644
--- a/config/AcediaSystem.ini
+++ b/config/AcediaSystem.ini
@@ -1,48 +1,57 @@
-; 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]
-; Unfortunately, thanks to the TWI's code, there's no way to catch events
+; 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
; type class.
-; As a workaround, Acedia can optionally replace bloat and siren damage
+; 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
+; 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
+; 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
+; 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"
diff --git a/sources/CoreRealm/SideEffects.uc b/sources/CoreRealm/SideEffects.uc
new file mode 100644
index 0000000..104ab12
--- /dev/null
+++ b/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 .
+ */
+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
+}
\ No newline at end of file
diff --git a/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc b/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc
index c55c7a8..7ff559f 100644
--- a/sources/Gameplay/KF1Frontend/Health/KF1_HealthComponent.uc
+++ b/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`.")
}
\ No newline at end of file
diff --git a/sources/Unreal/BroadcastsAPI/BroadcastAPI.uc b/sources/Unreal/BroadcastsAPI/BroadcastAPI.uc
index 23d653b..5504c37 100644
--- a/sources/Unreal/BroadcastsAPI/BroadcastAPI.uc
+++ b/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 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.")
}
\ No newline at end of file
diff --git a/sources/Unreal/BroadcastsAPI/BroadcastEventsObserver.uc b/sources/Unreal/BroadcastsAPI/BroadcastEventsObserver.uc
index f0d1350..71b3fa6 100644
--- a/sources/Unreal/BroadcastsAPI/BroadcastEventsObserver.uc
+++ b/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
+ blockAllowsBroadcast = false
}
\ No newline at end of file
diff --git a/sources/Unreal/GameRulesAPI/GameRulesAPI.uc b/sources/Unreal/GameRulesAPI/GameRulesAPI.uc
index 2021f24..c736374 100644
--- a/sources/Unreal/GameRulesAPI/GameRulesAPI.uc
+++ b/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);
+ }
}
/**
@@ -390,5 +405,7 @@ public final function bool AreAdded(
defaultproperties
{
- infoAddedGameRules = (l=LOG_Info,m="Added AcediaCore's `AcediaGameRules`.")
+ 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.")
}
\ No newline at end of file