Startup event for actors spawned by a mutator

A refuge for those migrating from the fallen DXEditing.com and a place for general discussion relating to Deus Ex editing (coding, mapping, etc).
Post Reply
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Startup event for actors spawned by a mutator

Post by Hanfling »

I had this strange issue which seems like the gun of an AutoTurret which i replaced didn't vanished but sticked around.. however further investigation revealed a far larger general problem.

When you use a Mutator to replace (preplaced) actors on a map, or otherwise spawn actors during *BeginPlay() the *BeginPlay() methods will be called multiple times. A quick setup is attached in TF.zip to observe this. However the log yields:
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.PreBeginPlay()
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.BeginPlay()
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.PostBeginPlay()
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.PostPostBeginPlay()
[..]
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.PreBeginPlay()
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.BeginPlay()
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.PostBeginPlay()
ScriptLog: 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0.PostPostBeginPlay()
Why this happens is simple: When you call spawn() it will call all the *BeginPlay() events. And add 01_NYC_UNATCOIsland.TFCrateExplosiveSmall0 to the actor list over which is just iterated for the PreBeginPlay() calls (which will call mutators CheckReplacement() function if prequisites are met). So PreBeginPlay() will even be called for this new actor. Then the other events are run. So the code will run twice.

I guess that when you spawn the actor during PostBeginPlay() just PostBeginPlay() and PostPostBeginPlay() events are run two times.

Anyway, this can lead to very very subtile bugs. e.g. the AutoTurret spawns it's gun twice, pawns will add themself two times to the PawnList, etc.

I guess i will have to start checking over all my actors startup event flow to make sure that everythings fine, probably this will fix some very strange bugs i still encounter.
Attachments
TF.zip
(1.18 KiB) Downloaded 432 times
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
User avatar
bjorn98009_91
Silhouette
Posts: 688
Joined: Thu May 08, 2008 8:17 am
Location: Hufvudstaden, Sweden
Contact:

Re: Startup event for actors spawned by a mutator

Post by bjorn98009_91 »

Never got the whole mutator business, what do mutators do?

Also pretty bad to have to objects in the map with identical names, if that's what's going on. Names are unique identifiers and if you have two movers with the same name the game could crash if you destroy a mover due to MakeGroupStop that is called recursively on a linked list, a list that is wrapping around on itself if you have dupes.
Producer and Quality Assurance Manager for Deus Ex: Revision.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Startup event for actors spawned by a mutator

Post by Hanfling »

Mutators do "little" modifications to the game. This could include modyfing inventory of a Pawn. Removing Actors and up to actually replacing actors. (e.g. I replace nearly all actor which are preplaced on the map in my coop mod with a mutator).

http://wiki.beyondunreal.com/Legacy:Mutator_Topics

There are no two objects with the same name around, it's the same actor which prints these messages.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
User avatar
bjorn98009_91
Silhouette
Posts: 688
Joined: Thu May 08, 2008 8:17 am
Location: Hufvudstaden, Sweden
Contact:

Re: Startup event for actors spawned by a mutator

Post by bjorn98009_91 »

Ah, but how does the mutator actually get injected to run? I've read something about CheckReplacement() but who is calling the mutator? Seems like there is some sort of list and each class that inherits the mutator class is run, is that's what's going on?
There are no two objects with the same name around, it's the same actor which prints these messages.
Gotcha.
Producer and Quality Assurance Manager for Deus Ex: Revision.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Startup event for actors spawned by a mutator

Post by Hanfling »

Actor.uc

Code: Select all

event PreBeginPlay()
{
	// [...]

	// Handle autodestruction if desired.
	if( !bGameRelevant && (Level.NetMode != NM_Client) && !Level.Game.IsRelevant(Self) )
		Destroy();
}

GameInfo.uc

Code: Select all

function bool IsRelevant( actor Other )
{
	local byte bSuperRelevant;

	// let the mutators mutate the actor or choose to remove it
	if ( BaseMutator.AlwaysKeep(Other) )
		return true;
	if ( BaseMutator.IsRelevant(Other, bSuperRelevant) )
	{
		if ( bSuperRelevant == 1 ) // mutator wants to override any logic in here
			return true;
	}
	else return false;

	// [...]

	return True;
}

event InitGame( string Options, out string Error )
{
	local string InOpt, LeftOpt;
	local int pos;
	local class<Mutator> MClass;

	// [...]

	BaseMutator = spawn(MutatorClass);
	log("Base Mutator is "$BaseMutator);
	InOpt = ParseOption( Options, "Mutator");
	if ( InOpt != "" )
	{
		log("Mutators"@InOpt);
		while ( InOpt != "" )
		{
			pos = InStr(InOpt,",");
			if ( pos > 0 )
			{
				LeftOpt = Left(InOpt, pos);
				InOpt = Right(InOpt, Len(InOpt) - pos - 1);
			}
			else
			{
				LeftOpt = InOpt;
				InOpt = "";
			}
			log("Add mutator "$LeftOpt);
			MClass = class<Mutator>(DynamicLoadObject(LeftOpt, class'Class'));	
			BaseMutator.AddMutator(Spawn(MClass));
		}
	}

	// [...]
}
The Mutators are basically some kind of a linked list.
Mutator.uc

Code: Select all

function bool AlwaysKeep(Actor Other)
{
	if ( NextMutator != None )
		return ( NextMutator.AlwaysKeep(Other) );
	return false;
}

function bool IsRelevant(Actor Other, out byte bSuperRelevant)
{
	local bool bResult;

	// allow mutators to remove actors
	bResult = CheckReplacement(Other, bSuperRelevant);
	if ( bResult && (NextMutator != None) )
		bResult = NextMutator.IsRelevant(Other, bSuperRelevant);

	return bResult;
}
So it's end up that Mutator.CheckReplacement() is called, which you can implement your Mutator stuff. Or if you want to enforce the actor to stick around you would override Mutator.AlwaysKeep().

To hook an own mutator in you can use the BaseMutator setting in your gameinfo, add the options to the URL, or spawn your mutator explicit for your gameinfo, this would be handy when you have more then one mutator. e.g.

HXGameInfo.uc

Code: Select all

// ---------------------------------------------------------------------
// InitGame()
// ---------------------------------------------------------------------

event InitGame( string Options, out string Error )
{
	// [...]

	Super.InitGame(Options, Error);

	// [...]

	BaseMutator.AddMutator(Spawn(class'HXMutator'));

	// [...]
}
However:
For classes which skip the Mutator logic, or you want to modify them before their PreBegin() call, you can do this in GameInfo's InitGame(), or in Mutators.*BeginPlay() events. However, the mutator events will be called twice, so make sure you wrote code which will not break when run twice. This spot is kinda nice when you want to use spawnnotifies. However when you want to catch actors spawned as early as PreBeginPlay(), like the AutoTurretGun, you will need to Link the spawnnotify in manually.

HXMutator.uc

Code: Select all

event PreBeginPlay()
{
	local PawnGenerator PawnGen;
	local ThrownProjectile ThrownProj;
	local ElectricityEmitter Emitter;
	local HXSpawnNotify SpawnNotify;

	//local Dispatcher Disp;

	foreach AllActors( class'DeusExLevelInfo', Info )
		break;

	if ( Info != None && localURL == "" )
		LocalURL = Caps(Info.mapName);

	// hackish (move to a spawn notify!)
	foreach AllActors( class'PawnGenerator', PawnGen )
		SetupPawnGenerator( PawnGen );

	foreach AllActors( class'ThrownProjectile', ThrownProj )
	{
		if ( !ReplaceThrownProjectile( ThrownProj ) )
			ThrownProj.Destroy();
	}

	foreach AllActors( class'ElectricityEmitter', Emitter )
	{
		if ( !ReplaceElectricityEmitter( Emitter ) )
			Emitter.Destroy();
	}

	if ( localURL == "04_NYC_BATTERYPARK" )
	{
		Log( "TODO: Spawn BUZZ Sound Actors for 04_NYC_BATTERYPARK" );
	}

	SpawnNotify = Spawn(class'HXAutoTurretGunSpawnNotify');
	SpawnNotify.Link();
}
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
User avatar
bjorn98009_91
Silhouette
Posts: 688
Joined: Thu May 08, 2008 8:17 am
Location: Hufvudstaden, Sweden
Contact:

Re: Startup event for actors spawned by a mutator

Post by bjorn98009_91 »

I see. Never worked with SpawnNotify either, and I haven't seen a DX mod that uses either SpawnNotify or Mutators.
Producer and Quality Assurance Manager for Deus Ex: Revision.
Post Reply