From 5a118a5b23a57985d66a0dcc08ee2f21ddd98f74 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Sun, 4 Apr 2021 03:12:40 +0700 Subject: [PATCH] Add `FixLogSpam` feature --- sources/FixLogSpam/FixLogSpam.uc | 62 +++++ sources/FixLogSpam/SpamPickup/HelperPickup.uc | 213 ++++++++++++++++++ .../MutatorListener_FixLogSpam_Pickup.uc | 40 ++++ .../FixLogSpam/SpamPickup/PickupSpamRule.uc | 38 ++++ sources/Manifest.uc | 21 +- 5 files changed, 364 insertions(+), 10 deletions(-) create mode 100644 sources/FixLogSpam/FixLogSpam.uc create mode 100644 sources/FixLogSpam/SpamPickup/HelperPickup.uc create mode 100644 sources/FixLogSpam/SpamPickup/MutatorListener_FixLogSpam_Pickup.uc create mode 100644 sources/FixLogSpam/SpamPickup/PickupSpamRule.uc diff --git a/sources/FixLogSpam/FixLogSpam.uc b/sources/FixLogSpam/FixLogSpam.uc new file mode 100644 index 0000000..93fbb38 --- /dev/null +++ b/sources/FixLogSpam/FixLogSpam.uc @@ -0,0 +1,62 @@ +/** + * This feature fixes different instances of log spam by the killing floor + * with various warnings and errors. Some of them have actual underlying bugs + * that need to be fixed, but a lot seem to be just a byproduct of dead and + * abandoned features or simple negligence. + * Whatever the case, now that TWI will no longer make any new changes to + * the game a lot of them do not serve any purpose and simply pollute + * log files. We try to get rid of at least some of them. + * Since changes we make do not actually have gameplay effect and + * are more aimed at convenience of server owners, our philosophy with the + * changes will be to avoid solutions that are way too "hacky" and prefer some + * message spam getting through to the possibility of some unexpected gameplay + * effects as far as vanilla game is concerned. + * Copyright 2021 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 FixLogSpam extends Feature + config(AcediaFixes); + +// This is responsible for fixing log spam due to picking up dropped +// weapons without set `inventory` variable. +var private const bool fixPickupSpam; +var private HelperPickup helperPickupSpam; + +protected function OnEnabled() +{ + if (fixPickupSpam) { + helperPickupSpam = HelperPickup(_.memory.Allocate(class'HelperPickup')); + } +} + +protected function OnDisabled() +{ + _.memory.Free(helperPickupSpam); + helperPickupSpam = none; +} + +public function Tick(float delta) +{ + if (helperPickupSpam != none) { + helperPickupSpam.Tick(); + } +} + +defaultproperties +{ + fixPickupSpam = true +} \ No newline at end of file diff --git a/sources/FixLogSpam/SpamPickup/HelperPickup.uc b/sources/FixLogSpam/SpamPickup/HelperPickup.uc new file mode 100644 index 0000000..363f7cc --- /dev/null +++ b/sources/FixLogSpam/SpamPickup/HelperPickup.uc @@ -0,0 +1,213 @@ +/** + * Helper object for `FixLogSpam` fix, responsible for fixing log spam + * due to picking up dropped weapons without set `inventory` variable. + * Copyright 2021 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 HelperPickup extends AcediaObject + config(AcediaFixes); + +/** + * `KFWeaponPickup` class is responsible for spamming log with + * error messages about missing `inventory` actor: "Accessed None 'Inventory'". + * This is caused by `Destroyed()` event that tries to call `KFGameType`'s + * `WeaponDestroyed()` on pickup's `inventory.class`, but it fails because + * `inventory` is essentially guaranteed to be `none` for pickups dropped + * by players. Since no `inventory != none` check is made, this leads to + * log message spam. + * To fix it we simply disable `Destroyed()` event altogether, since all + * it does is: + * 1. Fails as we have described; + * 2. In `super(Pickup).Destroyed()` sets `myMarker.markedItem` to `none`, + * which does nothig for pickups dropped by players, since they do not + * have any `myMarker` in the first place: they are usually defined to + * refer to `InventorySpot`s, generated for placed pickups when + * navigation paths are built. + * + * The are two issues that need resolving for this solution to work: + * 1. Distinguish between pickups dropped by the player versus pickups + * placed on the map; + * 2. Even if one disables `Destroyed()` event, it can get re-enabled again + * (possibly by a state change). + * One way to accomplish the first goal is to check `instigator` during + * `CheckReplacement()` event: it should be `none` for map pickups and point + * to the player's pawn for dropped pickups. If, for some reason, instigator + * would end up being equal to `none` for a dropped pickup, we also record all + * the other pickups and give them some time to initialize + * (`CheckReplacement()` is called at the moment they are spawned and + * before initialization logic is completed) and check their `bDropped` flag, + * since it is what decides whether log message about accessing `none`-value + * will be output: `if ( bDropped && class(Inventory.Class) != none )`. + * Then we also try to fix all the weapons with `bDropped == true`. + * To resolve issue with `Destroyed()` event being re-enabled we use + * brute force approach of disabling it every tick: + * 1. Amount of dropped pickups is small enough for it to not cause + * performance issues; + * 2. Checking change in state or trying to determine other causes for + * re-enabling the event would likely just lead to more complicated + * logic with around the same or even worse performance. + * We also enforce additional update when we catch player trying to pickup some + * `KFWeaponPickup` with `GameRules`'s `OverridePickupQuery()`, which is + * called soon before pickup's destruction. Note that this update cannot + * replace update inside the `Tick()`, since pickup can be destroyed without + * being picked up, i.e. at the start of each wave. + * + * Described method does not 100% guarantee removal of the related spam + * messages, but should make them virtually impossible. At least on + * vanilla servers. + */ + +// For easy access in relevant `GameRules` and mutator event listener. +var private HelperPickup singletonInstance; +// Already fixed pickups that we periodically "refix". +var private array recordedPickups; +// Pickups that will get `bDropped` flag checked the next tick to +// determine if we need to fix them. +var private array pendingPickups; + +protected function Constructor() +{ + local LevelInfo level; + local KFWeaponPickup nextPickup; + if (default.singletonInstance == none) { + default.singletonInstance = self; + } + // To detect when player tries to pick something up + // (and force additional pickup fix update) + _.unreal.AddGameRules(class'PickupSpamRule'); + // To detect newly spawned pickups + class'MutatorListener_FixLogSpam_Pickup'.static.SetActive(true); + // Find all `KFWeaponPickup`s laying around on the map, + // so that we can fix preexisting ones too. + // But add them to pending list in a freaky case this `HealperPickup` + // was created during one's initialization. This will give it time to + // set up variables that distinguish dropped pickup from another + // kind of pickup. + level = _.unreal.GetLevel(); + foreach level.DynamicActors(class'KFMod.KFWeaponPickup', nextPickup) { + pendingPickups[pendingPickups.length] = nextPickup; + } +} + +protected function Finalizer() +{ + local int i; + for (i = 0; i < recordedPickups.length; i += 1) + { + if (recordedPickups[i] != none && !recordedPickups[i].bPendingDelete) { + recordedPickups[i].Enable('Destroyed'); + } + } + recordedPickups.length = 0; + pendingPickups.length = 0; + _.unreal.RemoveGameRules(class'PickupSpamRule'); + class'MutatorListener_FixLogSpam_Pickup'.static.SetActive(false); +} + +public final static function HelperPickup GetInstance() +{ + return default.singletonInstance; +} + +// Checks whether given pickup is recorded in any of our records, +// including pending pickups. +// `none` is never recorded. +private final function bool IsPickupRecorded(KFWeaponPickup pickupToCheck) +{ + local int i; + if (pickupToCheck == none) { + return false; + } + for (i = 0; i < recordedPickups.length; i += 1) + { + if (recordedPickups[i] == pickupToCheck) { + return true; + } + } + for (i = 0; i < pendingPickups.length; i += 1) + { + if (pendingPickups[i] == pickupToCheck) { + return true; + } + } + return false; +} + +// Gets rid of pickups that got destroyed at some point and now are set +// to `none`. +private final function CleanRecordedPickups() +{ + local int i; + while (i < recordedPickups.length) + { + if (recordedPickups[i] == none) { + recordedPickups.Remove(i, 1); + } + else { + i += 1; + } + } +} + +// Makes helper handle given pickup `newPickup` by either fixing it or +// delaying to check whether it is dropped. +public final function HandlePickup(KFWeaponPickup newPickup) +{ + if (newPickup == none) return; + if (IsPickupRecorded(newPickup)) return; + + if (newPickup.instigator != none) + { + newPickup.Disable('Destroyed'); + recordedPickups[recordedPickups.length] = newPickup; + } + else { + pendingPickups[pendingPickups.length] = newPickup; + } +} + +// Re-disables `Destroyed()` event for all recorded (confirmed) dropped pickups +// and processes all pending (for the check if they are dropped) pickups. +public final function UpdatePickups() +{ + local int i; + for (i = 0; i < recordedPickups.length; i += 1) + { + if (recordedPickups[i] != none) { + recordedPickups[i].Disable('Destroyed'); + } + } + for (i = 0; i < pendingPickups.length; i += 1) + { + if (pendingPickups[i].bDropped) + { + pendingPickups[i].Disable('Destroyed'); + recordedPickups[recordedPickups.length] = pendingPickups[i]; + } + } + pendingPickups.length = 0; +} + +public final function Tick() +{ + CleanRecordedPickups(); + UpdatePickups(); +} + +defaultproperties +{ +} \ No newline at end of file diff --git a/sources/FixLogSpam/SpamPickup/MutatorListener_FixLogSpam_Pickup.uc b/sources/FixLogSpam/SpamPickup/MutatorListener_FixLogSpam_Pickup.uc new file mode 100644 index 0000000..f47ef5b --- /dev/null +++ b/sources/FixLogSpam/SpamPickup/MutatorListener_FixLogSpam_Pickup.uc @@ -0,0 +1,40 @@ +/** + * Overloaded mutator events listener to catch and, possibly, + * prevent spawning `KFWeaponPickup` for fixing log spam related to them. + * Copyright 2021 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 MutatorListener_FixLogSpam_Pickup extends MutatorListenerBase + abstract; + +static function bool CheckReplacement(Actor other, out byte isSuperRelevant) +{ + local HelperPickup helper; + local KFWeaponPickup otherPickup; + otherPickup = KFWeaponPickup(other); + if (otherPickup == none) return true; + helper = class'HelperPickup'.static.GetInstance(); + if (helper == none) return true; + + helper.HandlePickup(otherPickup); + return true; +} + +defaultproperties +{ + relatedEvents = class'MutatorEvents' +} \ No newline at end of file diff --git a/sources/FixLogSpam/SpamPickup/PickupSpamRule.uc b/sources/FixLogSpam/SpamPickup/PickupSpamRule.uc new file mode 100644 index 0000000..f2fd8b7 --- /dev/null +++ b/sources/FixLogSpam/SpamPickup/PickupSpamRule.uc @@ -0,0 +1,38 @@ +/** + * This rule detects when someone is trying to... pick up a pickup and + * forces additional fixing update on all the ones, dropped by players. + * Copyright 2021 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 PickupSpamRule extends GameRules; + +function bool OverridePickupQuery( + Pawn toucher, + Pickup touchedPickup, + out byte allowPickup) +{ + local HelperPickup helper; + helper = class'HelperPickup'.static.GetInstance(); + if (helper != none) { + helper.UpdatePickups(); + } + return super.OverridePickupQuery(toucher, touchedPickup, allowPickup); +} + +defaultproperties +{ +} \ No newline at end of file diff --git a/sources/Manifest.uc b/sources/Manifest.uc index 538e6ab..8f87fd2 100644 --- a/sources/Manifest.uc +++ b/sources/Manifest.uc @@ -22,14 +22,15 @@ defaultproperties { - features(0) = class'FixZedTimeLags' - features(1) = class'FixDoshSpam' - features(2) = class'FixFFHack' - features(3) = class'FixInfiniteNades' - features(4) = class'FixAmmoSelling' - features(5) = class'FixSpectatorCrash' - features(6) = class'FixDualiesCost' - features(7) = class'FixInventoryAbuse' - features(8) = class'FixProjectileFF' - features(9) = class'FixPipes' + features(0) = class'FixZedTimeLags' + features(1) = class'FixDoshSpam' + features(2) = class'FixFFHack' + features(3) = class'FixInfiniteNades' + features(4) = class'FixAmmoSelling' + features(5) = class'FixSpectatorCrash' + features(6) = class'FixDualiesCost' + features(7) = class'FixInventoryAbuse' + features(8) = class'FixProjectileFF' + features(9) = class'FixPipes' + features(10) = class'FixLogSpam' } \ No newline at end of file