Page 1 of 2

WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 9:19 am
by Major Cooke
Pull Request

This solves an issue where a potentially complex WorldThing* event could be called for potentially LOTS of actors at once, causing one hell of a notable dip in FPS. For example, AEons of Death has directors -- the Left 4 Dead director in particular at any time could spawn 100-600 zombies if a horde is suddenly unleashed. This horde of common infected, if blasted with a big enough explosion, could kill them all at once, and each one spawns ~4-8 gibs at once. THOUSANDS of spawned actors in an instant.

Giving the gibs NOEVENTSPAWN and NOEVENTDESTROY worked extremely well at preventing that lag spike, along with all of the special effects that would have nothing to do with the gameplay otherwise.

And lets not forget, more than one event handler can be present, especially if addons are loaded.

There are 5 in total, all prefixed with NOEVENT:
  • SPAWN
  • DEATH
  • DAMAGE
  • REVIVE
  • DESTROY
The revive flag will also be checked before having it's flags reset (and on that note, I noticed that flags8 was not in the revival code and fixed it).

I also do not believe they should be merged into one flag at all. Some events may still be desired while others are exempt from calling.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 11:18 am
by Graf Zahl
I hope this doesn't end up like the pain stuff with exceptions of exceptions of exceptions. Wanna bet that some people will request an override? And wanna bet that some people will use this as a means to outmaneuver mods that use event handlers?

Therefore I'd like to hear a second opinion first.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 11:52 am
by Major Cooke
Making further exceptions is not only unneeded, it's utterly pointless. One can just do this:

Code: Select all

let event = SomeEventHandler(EventHandler.Find("SomeEventHandler"));
and then call a function in the handler if they need it. Or ultimately, they can just remove, say, the NOEVENTSPAWN flag in BeginPlay based on conditions.

And I have no further plans dealing with events after this submission. This is simply it, because calling this every single time something is destroyed:

Code: Select all

    override void WorldThingDestroyed(WorldEvent e)
    {
        Actor mo = e.Thing;
        
        if (mo.bNOINTERACTION || mo.bMISSILE || mo.bNOSECTOR ||
            mo is "AEoDMonsterSpawner" || mo is "DirectorBase")    
            return;
        if (mo is "SpecialMonsterBase")
        {
            if (Spec.Size())
            {
                int size = Spec.Size();
                int pos = Spec.Find(mo);
                if (pos < size)            Spec.Delete(pos);
            }
        }
        else if (mo.bISMONSTER || mo.bCORPSE)
        {
            if (Monsters.Size())
            {
                int size = Monsters.Size();
                int pos = Monsters.Find(mo);    
                if (pos < size)            Monsters.Delete(pos);
            }
        }
        else if (mo is "Inventory")
        {
            if (Absorbables.Size())
            {
                int size = Absorbables.Size();
                int pos = Absorbables.Find(mo);    
                if (pos < size)            Absorbables.Delete(pos);
            }
        }
    }
...absolutely dropkicks performance. Especially if there's more than one event handler or WorldThingSpawned is done, or both!

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 12:14 pm
by Gutawer
I'm very much not a fan of this. Part of the reason why these events are useful at all is because they are guaranteed to happen when they need to, so mod behaviour can rely on things being properly set up by these events. If an actor can just disable certain events from occurring because it causes lag (which, I would consider a fault of the mod itself, not the event handler system), then that guarantee is lost and modding becomes a lot trickier for stuff that needs to 100% happen. To me, this would seem like a feature suggestion to disable ENTER scripts for certain player classes, which would obviously break map behaviour in many places, and I'd consider this to have the same harmful effect.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 12:15 pm
by ZZYZX
I don't like this.

Quote from Discord
[9:12 PM] ZZYZX: WorldThing events are created so that custom mods can do stuff to arbitrary other mods
[9:12 PM] ZZYZX: In an universal way
[9:12 PM] ZZYZX: What you propose will cause things like
[9:12 PM] ZZYZX: mymod.pk3 and mymod_aeod_compat_patch.pk3
[9:13 PM] ZZYZX: Because inheritance is not possible when the parent actor is not defined... which means the inheritance patch cannot be loaded by default
[9:13 PM] ZZYZX: Which basically breaks the whole point of having an event handler
[9:13 PM] ZZYZX: So please find another reason why it lags and don't do this
Please provide minimal example of laggy code so that it can be debugged. I don't understand why 500*4 "if..return" calls in a tic should bring FPS to zero.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 12:38 pm
by Major Cooke
I'll make one.

But in the mean time, perhaps a blacklist/whitelist approach like inventory items have Forbidden/RestrictedTo would be better.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 12:42 pm
by Graf Zahl
ZZYZX wrote:I don't understand why 500*4 "if..return" calls in a tic should bring FPS to zero.
They probably don't. But executing 500 event handlers can surely have a devastating impact. Major Cooke is correct about one thing: If these events are being called on every chunk of debris that gets spawned, destroyed or mutilated they surely will kill performance quite efficiently.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 12:48 pm
by Major Cooke
Well, more like 2 event handlers where WorldThingSpawned and WorldThingDestroyed are both called so that quickly adds up.

So I'm wondering if perhaps a blacklist approach similar to Forbidden/RestrictedTo would do the trick?

And yes, it is being called on everything that spawns and destroys.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 12:53 pm
by ZZYZX
This script creates 5000 actors all of which are registered by two handlers (verified using counter). Does not lag.
Each actor spawns, waits for 1 tick, spawns more actors and dies. First 500 are spawned (by summon testactor), then each of these spawns 10 more.

I also bound "summon testactor" to a key and could not produce any noticeable FPS drop by spamming that key.

FPS drop only becomes apparent when I crank the first number up to 5000 (5000 then each spawns 10), but that's... not very good either way. nuts.wad is not recommended.

Expected log (from 5000) looks like this: https://i.imgur.com/Qta3Puk.png

This is to back my point that I don't understand why it should lag. It does almost nothing if the first thing in the function is "if actor should not be handled".

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 1:14 pm
by Major Cooke
I'll be more specific: the lag comes from the compounding dual event handlers spawning and destroying calls happening. And specifically, I'm recording them to an array of interactable entities.

Whenever they're spawned, they're pushed to an array in ONE event handler:

Code: Select all

Array<Actor> FX;
Array<SpecialMons> Spec;
Array<Actor> Monsters;
Array<Inventory> Absorbables;

override void WorldThingSpawned(WorldEvent e)
{
	Actor mo = e.Thing;
	if (!mo || mo.bNOINTERACTION || mo.bNOBLOCKMAP || mo.bNOSECTOR ||
		mo is "AEoDMonsterSpawner" || mo is "DirectorBase")
	{
		return;
	}
	if (mo.bISMONSTER || mo.bCORPSE)
	{
		if (mo is "SpecialMonsterBase")
		{
			let SpecialMon = SpecialMonsterBase(mo);
			if (SpecialMon)
			{
				SpecialMon.Tracker = self;
				Spec.Push(SpecialMon);
			}
		}
		else if (Spec.Size())	Monsters.Push(mo);
	}
	else if (mo is "Inventory" && mo.species == 'Absorbable')
	{
		Absorbables.Push(mo);
	}
	else if (mo is "PlayerPawn" && Spec.Size())
	{
		Monsters.Push(mo);
	}
}
While another does this:

Code: Select all

override void WorldThingSpawned(WorldEvent e)
{
	if (DemonMorph)
	{
		Actor mo = e.Thing;
		if (!mo || mo.bNOINTERACTION || mo is "DirectorBase")	return;
		if (mo.bISMONSTER && mo.health > 0)
		{
			mo.A_GiveInventory("DemonMorphTranslationItem", 1);
		}
	}
}
...Hmm, I think the culprit might be the array functions...

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 1:27 pm
by Graf Zahl
The culprit is that you actually *DO* something in your handler. Of course, with a nearly empty handler the impact will be considerably less severe.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 1:37 pm
by ZZYZX
Ok, I added array access, it now reads/writes all secondary actors to an array. Logs array size of 5000. Still no lag. I didn't measure exact millisecond timing but by FPS counter it takes approximately 10-17ms on the "hard" frames.

So while overall I understand that this might be caused by lag accumulation in AEoD (due to AEoD being complex enough for lag accumulation to be possible), but I don't think this is an event handlers problem.

upd: I'll try to hack the full AEoD version of this code tomorrow once I'm a bit more awake. Will know what's wrong better at that point. I hope I provided enough evidence that it's not as simple as event handlers being laggy here so that it doesn't get changed until more is known :P

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 2:42 pm
by Major Cooke
The thing about AEoD is, it's very hands on about the effects in game.

There can be a TON of effects too, but that doesn't cause the same notable amount of lag as including the event handlers on top of it. And those spawn/destroy functions are called on every actor that spawns.

Which is why I now propose a blacklist/whitelist approach for actors.

Code: Select all

BlacklistHandler "Handler1", "Handler2", ...;
Or blacklisting which handler's functions are allowed to trigger:

Code: Select all

BlacklistHandlerSpawn "Handler1", ...;
Thoughts? Ideas?

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 3:10 pm
by Graf Zahl
I'm sorry but that's not going to happen. Seriously, the only solution here can be: Do not use event handlers if you spawn tons of stuff.

In retrospect I have to say that it was a stupid idea to have an event handler being unconditionally called whenever ANY actor gets spawned. This should have been restricted from the start to 'important' actors like monsters and inventory stuff and for the test been purely opt-in.

Re: WorldThing Event Disabling Flags

Posted: Tue Apr 10, 2018 4:48 pm
by Major Cooke
I agree that it being an opt-in would have been nice, but limiting it to just important actors would have only hurt creativity, resorting to hacks. A_RadiusGive certainly taught me that.