Legacy:ConfigMaster

From Unreal Wiki, The Unreal Engine Documentation Site
UT2003 :: Object >> Actor >> Info >> Mutator >> ConfigMaster (ConfigManager)

This is a custom class, which is part of the ConfigManager for UT2003.

//-----------------------------------------------------------
// ConfigManager.ConfigMaster version 1.8
// Allows for ServerActors & Mutators to add to PlayInfo
//
// The latest version of ConfigManager is always available at
// http://www.organized-evolution.com/ConfigManager/
//-----------------------------------------------------------

class ConfigMaster extends Mutator DependsOn(AutoLoader) config(ConfigMaster);

// If 2184 or earlier, crash the server
var int						GameVersion;

// Contains previous values of modified IniEntries, unless there is already
// a previous value for an IniEntry.  When a managed loader is de-activated,
// the original value of the IniEntry is restored.
var config array<AutoLoader.IniEntry>	OriginalValues;

// Holder for properties which aren't needed - used by loaders that get activated at the end of the match
var array<Property>			IrrelevantProperties;

struct SA
{
	var class<Info>			SAClass;
	var string				SAName;
	var string				SADescription;
};

struct AL
{
	var class<AutoLoader>	AutoLoaderClass;
	var string				AutoLoaderDesc;
};

var array<SA>				ManagedActors;		// Info about Loaders' ActorClasses
var array<AL>				LoaderClasses;		// All Loader Classes found
var array<AutoLoader>		Loaders;			// Active Loaders
var array<string>			ActiveMutators;		// Currently active mutator classes
var bool					bInitialized;		// Initialization
var bool					bHangRestart;		// Don't let server travel yet

var GameEngine				GE;

const VERSION = 1.71;
const LOGNAME = 'Config Manager';
var const bool	DEBUG;
var const bool 	DEBUGPROPS;

// Localization
var localized string LoadSuccess;
var localized string RequiredVersion;
var localized string UpgradeOrDie;
var localized string RemovalCancelled;

// Errors
var localized string InvalidLoaderClass;
var localized string InvalidPI;

event PreBeginPlay()
{
	GameVersion = int(Level.EngineVersion);

	log(" -"@LOGNAME@VERSION@LoadSuccess,LOGNAME);
	if (GameVersion < 2199)
	{
		log(RequiredVersion@LOGNAME$".",LOGNAME);
		log(UpgradeOrDie,LOGNAME);
		log("",'ConfigManager');
		Assert(False);
	}

	GetGameEngine();
}

event PostBeginPlay()
{
	local int i;

	if (DEBUG)
	{
		log(class@"Beginning initialization",'PostBeginPlay');
		log("START OF GAME SERVERACTORS",'PostBeginPlay');
		for (i = 0; i < GE.ServerActors.Length; i++)
			log("ServerActor["$i$"]:"$GE.ServerActors[i],'PostBeginPlay');

		log("START OF GAME SERVERPACKAGES",'PostBeginPlay');
		for (i = 0; i < GE.ServerPackages.Length; i++)
			log("ServerPackages["$i$"]:"$GE.ServerPackages[i],'PostBeginPlay');

		log("",'PostBeginPlay');
	}
//	Level.Game.AddMutator("ConfigManagerGUI.ConfigManagerGUIMaster");

	InitLoaders();
	if (DEBUG)
		log(class@"finished loading all actors and loaders",'PostBeginPlay');

	Super.PostBeginPlay();
}

// Sends a list of currently active mutators to all loader classes
// Loader classes may want to check if its mutator was loaded without using the loader
// (added at command line, etc.)
function GetGameEngine()
{
	foreach AllObjects(class'Engine.GameEngine', GE)
		break;
}

function AddMutator(Mutator M)
{
	local int i;

	for (i = 0; i < ActiveMutators.Length; i++)
		if (ActiveMutators[i] ~= string(M.Class))
			break;

	if (i == ActiveMutators.Length)
		ActiveMutators[ActiveMutators.Length] = string(M.Class);

	if (DEBUG)
		for (i = 0; i < ActiveMutators.Length; i++)
			log("ActiveMutators["$i$"]:"@ActiveMutators[i],'AddMutator');

	Super.AddMutator(M);
}

// Do not show ConfigMaster in server browser as active mutator
function GetServerDetails( out GameInfo.ServerResponseLine ServerState )
{
}

function InitLoaders()
{
	FindAllLoaders();
	CreateLoaders();
}

function FindAllLoaders()
{
	local int i, j;
	local AL TempAL;
	local SA TempSA;

	local class<AutoLoader>	LoaderClass;
	local class<Info>		ServerActorClass;
	local string			LoaderClassName, LoaderClassDescription,
				 			ActorClass, ServerActorName, ServerActorDesc;

	GetNextIntDesc("ConfigManager.AutoLoader",0,LoaderClassName,LoaderClassDescription);
	while (LoaderClassName != "")
	{
		// Hack to prevent Config Loader from being loaded
		if (LoaderClassName ~= "ConfigManagerLoader.SampleLoader")
		{
			GetNextIntDesc("ConfigManager.AutoLoader",++i,LoaderClassName,LoaderClassDescription);
			continue;
		}
		if (DEBUG)
			log("Found a new AutoLoader:"@LoaderClassName@LoaderClassDescription,'FindAllLoaders');

		LoaderClass = class<AutoLoader>(DynamicLoadObject(LoaderClassName,Class'Class'));
		if (LoaderClass != None)
		{
			if (ShouldAddLoaderClass(LoaderClass))
			{
				TempAL.AutoLoaderClass = LoaderClass;
				TempAL.AutoLoaderDesc = LoaderClassDescription;
				LoaderClasses[LoaderClasses.Length] = TempAL;
				j = -1;
				while (LoaderClass.static.AddManagedActor(j++, ActorClass, ServerActorName, ServerActorDesc))
				{
					if (ActorClass != "" && ServerActorName != "" && ServerActorDesc != "")
					{
						ServerActorClass = class<Info>(DynamicLoadObject(ActorClass,class'Class'));
						if (ServerActorClass != None)
						{
							TempSA.SAClass = ServerActorClass;
							TempSA.SAName = ServerActorName;
							TempSA.SADescription = ServerActorDesc;

							ManagedActors[ManagedActors.Length] = TempSA;
						}
					}
				}
			}
		}
		else Warn(InvalidLoaderClass@LoaderClassName);

		GetNextIntDesc("ConfigManager.AutoLoader",++i, LoaderClassName, LoaderClassDescription);
	}

	return;
}

function bool ShouldAddLoaderClass(class<AutoLoader> NewClass)
{
	local int i;

	if (DEBUG)
		log("Loader Class"@NewClass,'ShouldAddLoaderClass');

	// Don't add if it's already in the list
	for (i = 0;i < LoaderClasses.Length; i++)
		if (LoaderClasses[i].AutoLoaderClass == NewClass)
			return false;

	return true;
}

function CreateLoaders()
{
	local int i;
	local AutoLoader Loader;

	if (DEBUG)
		log("LoaderClasses"@LoaderClasses.Length,'CreateLoaders');

	for (i = 0;i<LoaderClasses.Length;i++)
	{
		if (ShouldAddLoader(LoaderClasses[i].AutoLoaderClass))
		{
			Loader = AddLoader(LoaderClasses[i].AutoLoaderClass);
			if (Loader != None)
				Loaders[Loaders.Length] = Loader;
		}
	}
}

function bool LoaderNotActive(class<AutoLoader> NewClass)
{
	local int i;

	for (i = 0; i < Loaders.Length; i++)
		if (Loaders[i].Class == NewClass)
			return false;

	return true;
}

function bool ShouldAddLoader(class<AutoLoader> NewClass)
{
	local bool bShouldAdd;

	bShouldAdd = LoaderNotActive(NewClass) && GE != None;

	if (bShouldAdd)
		bShouldAdd = NewClass.static.CheckStrayActors(GE.GetPropertyText("ServerActors")) || NewClass.static.IsActive();

	bShouldAdd = bShouldAdd && NewClass.static.ValidateLoader();

	if (DEBUG)
		log(NewClass@"will be added:"$bShouldAdd,'ShouldAddLoader');

	return bShouldAdd;
}

function AutoLoader AddLoader(class<AutoLoader> NewClass)
{
	local AutoLoader Loader;
	if (NewClass == None) return None;

	Loader = Spawn(NewClass,Self);
	if (Loader != None && GE != None)
		Loader.GE = GE;

	if (DEBUG)
		log("Returning"@Loader,'AddLoader');

	return Loader;
}

// Make sure to remain as Game.BaseMutator.NextMutator so we will know about every mutator that is added.
event Tick(float DeltaTime)
{
	local int i;

	if (bInitialized && Level != None && Level.NextURL != "")
	{
		if (bHangRestart)
			Level.NextSwitchCountdown = 4.0;

		else if (Loaders.Length > 0)
		{
			bHangRestart = True;
			SetTimer(0.2,False);
		}
	}

	// Do not replace BaseMutator, or DMMutator will show up in server browser as an active mutator
	if (Level != None && Level.Game != None && Level.Game.BaseMutator != None)
	{
		if (Level.Game.BaseMutator.NextMutator != None)
		{
			if (Level.Game.BaseMutator.NextMutator != Self)
			{
			// Some other ConfigManager as BaseMutator.NextMutator, but it isn't me????
			// Maybe added ConfigManager as a serveractor?
			// Disable or server will crash
				if (!bInitialized && Level.Game.BaseMutator.NextMutator.Class == Class)
				{
					Destroy();
					return;
				}

				// We have been replaced by another mutator wanting
				// to be the first mutator.
				if (Level.Game.BaseMutator.NextMutator.NextMutator == Self)
				{
					if (NextMutator != None)
						Level.Game.BaseMutator.NextMutator.NextMutator = NextMutator;
					else Level.Game.BaseMutator.NextMutator.NextMutator = None;
				}

				NextMutator = Level.Game.BaseMutator.NextMutator;
				Level.Game.BaseMutator.NextMutator = Self;
			}
		}

		else Level.Game.BaseMutator.NextMutator = Self;
	}

	if (!bInitialized)
	{
		for (i = 0; i < LoaderClasses.Length; i++)
		{
			if (LoaderNotActive(LoaderClasses[i].AutoLoaderClass) && LoaderClasses[i].AutoLoaderClass.static.CheckCurrentMutators(GetURLOption("Mutator")))
			{
				if (DEBUG)
					log("Adding previously non-active loader"@LoaderClasses[i].AutoLoaderClass,'Tick');
				Loaders[Loaders.Length] = AddLoader(LoaderClasses[i].AutoLoaderClass);
			}
		}

		bInitialized = True;
	}
}

event Timer()
{
	Super.Timer();
	ApplyLoaderSettings();
}

function array<Property> LoadProperties()
{
	local array<Property>	AllProperties;
	local array<class>		RelevantActors;
	local array<string>		RelevantPropNames;
	local class				RelevantClass;
	local Property P;
	local int i, j, k;

	// Build a array of properties which are relevant to the currently loaded loaders
	// so that the amount of time it will take each loader to update their settings will
	// be dramatically reduced -
	// Typical number of Property objects in the game: 200,000
	// Typical number of properties relevant to current loaders: 50


	// Start by first getting a list of actors/objects which will be relevant

	// All ManagedActors are relevant (Loader's ActorClass)
	for (i = 0; i < ManagedActors.Length; i++)
	{
		for (j = 0; j < RelevantActors.Length; j++)
			if (RelevantActors[j] == ManagedActors[i].SAClass)
				break;

		if (j < RelevantActors.Length)
			continue;

		RelevantActors[RelevantActors.Length] = ManagedActors[i].SAClass;
	}

	if (DEBUG)
		log("Loaders"@LoaderClasses.Length,'LoadProperties');

	for (i = 0; i < LoaderClasses.Length; i++)
	{
		// If ActorClass was already added to list, skip it
		for (j = 0; j < RelevantActors.Length; j++)
			if (string(RelevantActors[j]) ~= LoaderClasses[i].AutoLoaderClass.default.ActorClass)
				break;

		if (j == RelevantActors.Length)
		{
			if (LoaderClasses[i].AutoLoaderClass.default.ActorClass != "")
				RelevantClass = class(DynamicLoadObject(LoaderClasses[i].AutoLoaderClass.default.ActorClass,class'Class'));

			if (RelevantClass != None)
				RelevantActors[RelevantActors.Length] = RelevantClass;
		}

		// Begin building a list of property names to mark as relevant
		// We'll match these names to the Property.Name variable later when we iterate AllObjects for class'Property'
		for (j = 0; j < LoaderClasses[i].AutoLoaderClass.default.RequiredIniEntries.Length; j++)
		{

			for (k = 0; k < RelevantPropNames.Length; k++)
			{
				if (RelevantPropNames[k] == LoaderClasses[i].AutoLoaderClass.default.RequiredIniEntries[j].PropName)
					break;
			}

			if ( k == RelevantPropNames.Length )
			{
				RelevantPropNames[RelevantPropNames.Length] = LoaderClasses[i].AutoLoaderClass.default.RequiredIniEntries[j].PropName;
				if (DEBUGPROPS)
					log("Added new Relevant Property Name:"$RelevantPropNames[k],'LoadProperties');
			}

			RelevantClass = class(DynamicLoadObject(LoaderClasses[i].AutoLoaderClass.default.RequiredIniEntries[j].ClassFrom,Class'class',True));
			if (RelevantClass == None) continue;

			// Find out if this entry's associated class is already in the relevant actors list
			// If not, add it
			for (k = 0; k < RelevantActors.Length; k++)
			{
				if (DEBUGPROPS)
					log("Compare"@LoaderClasses[i].AutoLoaderClass.default.RequiredIniEntries[j].ClassFrom@"to Relevant Actor"@k$":"@RelevantActors[k],'LoadProperties');

				if (RelevantActors[k] == RelevantClass)
					break;
			}
			if (DEBUGPROPS)
				log("Breaking on"@k$", Total Relevant Actors:"@RelevantActors.Length,'LoadProperties');

			if (k < RelevantActors.Length)
				continue;

			RelevantActors[RelevantActors.Length] = RelevantClass;
			if (DEBUGPROPS)
				log("Added new relevant actor:"$LoaderClasses[i].AutoLoaderClass.default.RequiredIniEntries[j].ClassFrom,'LoadProperties');
		}
	}

	// Finished building relevant actors and relevant names
	// Iterate AllObjects, looking for Property objects
	foreach AllObjects(class'Property', P)
	{
		RelevantClass = None;
		// Some property types should be skipped
		if ( ValidProp(P) )
		{
			if (Class(P.Outer) != None) RelevantClass = Class(P.Outer);
			if (RelevantClass != None)
			{
				for (i = 0; i < RelevantActors.Length; i++)
			 	{
			 		// The Outer variable for properties is the class that the property is contained in
			 		// If this property's Outer is an object that is relevant, add it to the list
			 		if (RelevantClass == RelevantActors[i])
			 		{
			 			for (j = 0; j < RelevantPropNames.Length; j++)
			 				if (RelevantPropNames[j] == string(P.Name))
			 				{
			 					if (DEBUGPROPS)
			 						log("Adding Initial Relevant Property:"$P.Outer$"."$P.Name,'LoadProperties');
			 					AllProperties[AllProperties.Length] = P;
			 					break;
			 				}

			 			if (j < RelevantPropNames.Length)
			 				break;
			 		}
			 	}
			}

			// OK, this property's Outer wasn't relevant
			// Check the property against the relevant name list
		 	if (i == RelevantActors.Length)
		 	{
		 		for (i = 0; i < RelevantPropNames.Length; i++)
		 		{
				// If we find a match, then we should check the relevant objects list again,
				// in case the relevant object is a superclass of the property's Outer
		 			if (RelevantPropNames[i] == string(P.Name))
		 			{
						if (DEBUGPROPS)
							log("Compare Property:"@i@RelevantPropNames[i]@"to"@string(P.Name),'LoadProperties');

		 				for (j = 0; j < RelevantActors.Length; j ++)
		 				{
		 					if (RelevantClass == None) continue;

							if (DEBUGPROPS)
								log("Relevant Actor Check:"@j@RelevantActors[j]@"is child of"@class(P.Outer)@":"$ClassIsChildOf(RelevantActors[j],Class(P.Outer)),'LoadProperties');

		 					if (ClassIsChildOf(RelevantActors[j], RelevantClass))
		 					{
		 						if (DEBUGPROPS)
		 							log("Adding Additional Relevant Property:"$P.Outer$"."$P.Name,'LoadProperties');

		 						AllProperties[AllProperties.Length] = P;
		 						break;
		 					}
		 				}

		 				if (j < RelevantActors.Length)
		 					break;
		 			}
		 		}
		 	}
		}
	}

	return AllProperties;
}

// Update Properties array with newly loaded loaders
// No longer used
function CheckRelevantProperties(AutoLoader L, out array<Property> Props)
{
	local int i, j;
	local array<class> Classes, Outers;

	local class TempClass;
	local Property P;

	if (DEBUGPROPS) log("Checking relevant properties for new loader"@L,'CheckRelevantProperties');

	for (i = 0; i < Props.Length; i++)
	{
		TempClass = Class(Props[i].Outer);
		if (TempClass == None) continue;

		for (j = 0; j < Outers.Length; j++)
			if (TempClass == Outers[j])
				break;

		if (j < Outers.Length) continue;

		Outers[Outers.Length] = TempClass;
	}

	if (L.bIncludeServerActor && L.ActorClass != "")
	{
		if (DEBUGPROPS) log("Checking for existence of"@L.ActorClass@"properties",'CheckRelevantProperties');

		TempClass = class(DynamicLoadObject(L.ActorClass,class'Class',True));
		if (TempClass != None)
		{
			for (i = 0; i < Outers.Length; i++)
				if (TempClass == Outers[i])
					break;

			if (i == Outers.Length)
				Classes[Classes.Length] = TempClass;

			else if (DEBUGPROPS)
				log(TempClass@"has already been added to the properties array.",'CheckRelevantProperties');
		}
	}

	for (i = 0; i < L.RequiredIniEntries.Length; i++)
	{
		if (DEBUGPROPS) log("Checking for existence of relevant class"@L.RequiredIniEntries[i].ClassFrom,'CheckRelevantProperties');

		TempClass = class(DynamicLoadObject(L.RequiredIniEntries[i].ClassFrom,class'Class',True));
		if (TempClass != None)
		{
			for (j = 0; j < Classes.Length; j++)
				if (Classes[j] == TempClass) break;

			if (j < Classes.Length) continue;

			for (j = 0; j < Outers.Length; j++)
				if (Outers[j] == TempClass) break;

			if (j < Outers.Length) continue;

			Classes[Classes.Length] = TempClass;
		}
		else if (DEBUGPROPS)
			log("Could not load RequiredIniEntry["$i$"].ClassFrom:"@L.RequiredIniEntries[i].ClassFrom,'CheckRelevantProperties');
	}

	if (DEBUGPROPS && Classes.Length == 0)
		log("All relevant classes from this loader already existed in properties array",'CheckRelevantProperties');

	for (i = 0; i < Classes.Length; i++)
	{
		if (DEBUGPROPS)
		{
			log("");
			log("Adding properties from class"@Classes[i]@"to properties array.",'CheckRelevantProperties');
		}

		for (j = 0; j < IrrelevantProperties.Length; j++)
		{
			P = IrrelevantProperties[j];
			TempClass = Class(P.Outer);

			if (TempClass == None || TempClass != Classes[i]) continue;

			if (DEBUGPROPS) log("    Adding property"@P.Name,'CheckRelevantProperties');

			Props[Props.Length] = P;
			IrrelevantProperties.Remove(j--, 1);
		}
	}
}

// TODO: Add support for structs
final function bool ValidProp(Property P)
{
	return P.Class != class'ObjectProperty' &&
		   P.Class != class'DelegateProperty' &&
		   P.Class != class'PointerProperty' &&
		   P.Class != class'MapProperty' &&
		   P.Class != class'StructProperty';
}

// Fills PlayInfo for ServerActors
final function CallManagedActorPlayInfo(PlayInfo PI)
{
	local int i, j;

	for (i = 0; i < LoaderClasses.Length; i++)
	{
		if (DEBUG)
			log("Checking for LoaderClass"@i@"in playinfo:"$loaderclasses[i].autoloaderclass,'ManagedActorFillPlayInfo');

		if (NotInPlayInfo(PI,LoaderClasses[i].AutoLoaderClass))
		{
			if (DEBUG)
				log("Calling FillPlayInfo() for LoaderClass"@i$":"$loaderclasses[i].autoloaderclass,'ManagedActorFillPlayInfo');

			LoaderClasses[i].AutoLoaderClass.static.FillPlayInfo(PI);
			PI.PopClass();
		}

		for (j = 0; j < ManagedActors.Length; j++)
		{
			if (LoaderClasses[i].AutoLoaderClass.default.ActorClass != string(ManagedActors[j].SAClass))
				continue;

			if (DEBUG)
				log("Checking for ManagedActor"@j@"in playinfo:"$ManagedActors[j].SAClass,'ManagedActorFillPlayInfo');

			if (LoaderClasses[i].AutoLoaderClass.static.IsActive())
			{
				ManagedActors[j].SAClass.static.FillPlayInfo(PI);
				PI.PopClass();
			}
		}
	}
}

// Only webadmin calls Mutator.MutatorFillPlayInfo()?
function MutatorFillPlayInfo(PlayInfo PI)
{
	if (DEBUG)
		log("");

	if (DEBUG)
		log("Checking URL Option:"$GetURLOption("Mutator"),'MutatorFillPlayInfo');

	if (NextMutator != None)
		NextMutator.MutatorFillPlayInfo(PI);

	CallManagedActorPlayInfo(PI);
}

// Allows Loaders to adjust game configuration parameters
// Loaders provide info about which ini changes need to be made for the mod to work
// This allows other programs (such as Ladder) to only include the ini modifications
// which are applicable for the loaded packages
//
// (Ex: ServerActors, ServerPackages, QueryHandlerClasses, Applications)
final function ApplyLoaderSettings()
{
	local int 				i, Index;
	local array<Property>	CurrentProperties;
	local class Temp;

	// Checking for any loaders that have been enabled since last check
	// Otherwise, must wait 2 server restarts to apply new loader settings
	// (One to load loader and apply changes, another for changes to take effect)
	FindAllLoaders();
	CurrentProperties = LoadProperties();
	for ( i = 0; i < LoaderClasses.Length; i++)
	{
		if (DEBUG)
			log("LoaderClass"@i$":"@LoaderClasses[i].AutoLoaderClass,'ApplyLoaderSettings');

		if (ShouldAddLoader(LoaderClasses[i].AutoLoaderClass) || (LoaderClasses[i].AutoLoaderClass.static.CheckCurrentMutators(Level.NextURL) && LoaderClasses[i].AutoLoaderClass.static.ValidateLoader()))
			Loaders[Loaders.Length] = AddLoader(LoaderClasses[i].AutoLoaderClass);

		else if (LoaderNotActive(LoaderClasses[i].AutoLoaderClass))
			AddLoader(LoaderClasses[i].AutoLoaderClass).AcceptRemoval(CurrentProperties);
	}

	if (DEBUGPROPS)
	{
		log("*** Relevant Property Listing ***",'ApplyLoaderSettings');
		for (i = 0; i < CurrentProperties.Length;i++)
		{
			if (Class(CurrentProperties[i].Outer) != None && Temp != Class(CurrentProperties[i].Outer))
			{
				Temp = Class(CurrentProperties[i].Outer);
				log("",'ApplyLoaderSettings');
				log("Relevant properties from class"@Temp@"-",'ApplyLoaderSettings');
			}
			log("    "$currentproperties[i].name,'ApplyLoaderSettings');
		}
	}

	while (Index < Loaders.Length)
	{
		if (DEBUG)
			log("Loader"@Index$":"@Loaders[Index],'ApplyLoaderSettings');

		if (Loaders[Index].ApplyUpdate())
			Loaders[Index].UpdateConfiguration(CurrentProperties);

		else if (!Loaders[Index].AcceptRemoval(CurrentProperties))
			log(RemovalCancelled@Loaders[Index],'ConfigManager');

		Index++;
	}

	Disable('Tick');
	if (DEBUG)
	{
		log("",'ApplyLoaderSettings');
		log("END OF GAME SERVERACTORS",'ApplyLoaderSettings');
		for (i = 0; i < GE.ServerActors.Length; i++)
			log("ServerActor["$i$"]:"$GE.ServerActors[i],'ApplyLoaderSettings');

		log("END OF GAME SERVERPACKAGES",'ApplyLoaderSettings');
		for (i = 0; i < GE.ServerPackages.Length; i++)
			log("ServerPackages["$i$"]:"$GE.ServerPackages[i],'ApplyLoaderSettings');

		log("",'ApplyLoaderSettings');
	}
}

function bool NotInPlayInfo(PlayInfo PI, class<Info> NewInfo)
{
	local int i;
	if (PI == None)
	{
		Warn(InvalidPI);
		return false;
	}

	for (i=0;i<PI.InfoClasses.Length;i++)
	{
		if (PI.InfoClasses[i] == NewInfo)
			return false;
	}

	return true;
}

function array<string> GetAllManagedActors()
{
	local int i;
	local array<string> Arr;

	for (i = 0; i < LoaderClasses.Length; i++)
		Arr = AddS(Arr,LoaderClasses[i].AutoLoaderClass.static.GetManagedActors());

	return Arr;
}

static final function float GetConfigMasterVersion()
{
	return VERSION;
}

// following function copied from wUtils (El_Muerte[TDS])
static final function array<string> AddS(array<string> A, array<string> B)
{
  local int i;
  for (i = 0; i < B.length; i++)
  {
    A.length = A.length+1;
    A[A.length-1] = B[i];
  }
  return A;
}