Legacy:ConfigMaster
From Unreal Wiki, The Unreal Engine Documentation Site
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;
}