I love the smell of UnrealEd crashing in the morning. – tarquin

Legacy:AutoLoader

From Unreal Wiki, The Unreal Engine Documentation Site

Jump to: navigation, search
UT2003 :: Actor >> Info >> AutoLoader (Package: ConfigManager)

Here's an Legacy:AutoLoader/Example on how to use this class

  1. //-----------------------------------------------------------
  2. //      ConfigManager.AutoLoader
  3. //
  4. //      This class has two functions:
  5. //      1. Perform all required ini changes needed for your mod.  Will also UNDO
  6. //         any changes made to the ini if your mod is deactivated.
  7. //         Additional benefit of auto-installation of your mutator on a dedicated
  8. //         server (to enable set bEnableMyLoader=True in defaultproperties)
  9. //
  10. //      2.      Pass FillPlayInfo() calls to mutators which are not part of the game's
  11. //              mutator list (such as mutators which only require ServerActors= lines in ini)
  12. //
  13. //-----------------------------------------------------------
  14. class AutoLoader extends Info
  15.         abstract;
  16.  
  17. // Quick note about debugging - By setting DEBUG=True in defaultproperties of your
  18. // AutoLoader subclass when compiling, you can receive large amounts of debug data.
  19. // However, you will quickly encounter the 1024 byte limit if you use ucc.exe to
  20. // start the testing server.  By starting a server using the "ut2003 -server" command,
  21. // you will bypass this limit, since ut2003.exe does not have these limitations.
  22.  
  23. var const bool DEBUG;
  24. var const bool DEBUGPROPS;
  25.  
  26. var ConfigMaster                                Manager;                        // Pointer to ConfigMaster mutator
  27. var string                                              InteractionClass;       // Not yet implemented
  28.  
  29. //=======================================
  30. //      Loading ServerActors
  31. //=======================================
  32. // Pointer to GameEngine
  33. var GameEngine                                  GE;
  34.  
  35. // This loader includes a server-side only mutator or server actor
  36. // that will not be part of the mutator chain (will receive FillPlayInfo() calls)
  37. var() bool                                              bIncludeServerActor;
  38.  
  39. // Classname of the ServerActor this loader loads
  40. //      Same value as what would otherwise be your mod's
  41. //      ServerActors= line in the .ini file.
  42. var() string ActorClass;
  43.  
  44. // Friendly Name of the Server Actor class
  45. //      Used by webadmin/adminmenu as the name of the server actor
  46. var() localized string                  FriendlyName;
  47. var() localized string                  ActorDescription;
  48.  
  49. // Lock this loader to a certain version (or higher) of your mod
  50. var() string                    RequiredVersion;        // Required version of actor
  51. var() localized string  VersionWarning;         // Message to write to server log if version not enough
  52. var() localized string  DownloadMsg;            // Message to write to log
  53.  
  54. //=======================================
  55. //      Automatic configuration changes
  56. //=======================================
  57. // A single ini change
  58. struct IniEntry
  59. {
  60.         var() string ClassFrom; // Class which contains the setting we want to change
  61.         var() string PropName;  // Name of the variable we're trying to change
  62.         var() string PropValue; // Value to apply
  63. };
  64.  
  65. // Array of ini settings required by this mod
  66. var()   array<IniEntry>                                 RequiredIniEntries;
  67. var     array<Property>                                 Properties;
  68.  
  69.  
  70. //###################################################################
  71. //###################################################################
  72. //
  73. //  Public methods - should be subclassed to customize loader's response
  74.  
  75. // if return true, loader class will be spawned
  76. static function bool IsActive()
  77. {
  78.         return false;
  79. }
  80.  
  81. // should be subclassed - set loader to active/will be included next match
  82. static function bool EnableLoader(optional string SpecialActor)
  83. {
  84.         return false;
  85. }
  86.  
  87. // should be subclassed
  88. static function bool DisableLoader(optional string SpecialActor)
  89. {
  90.         return true;
  91. }
  92.  
  93. // called on all loader classes - used to activate loader based on active mutators
  94. static function bool CheckCurrentMutators(string URL)
  95. {
  96.         return false;
  97. }
  98.  
  99. // called on all loader classes which have bIncludeServerActor=True - used to activate loader based on manual ServerActor entry
  100. static function bool CheckStrayActors(string ServerActors)
  101. {
  102.         local int i;
  103.         local bool bAddMe;
  104.  
  105.         if (default.bIncludeServerActor)
  106.                 if (InStr(ServerActors,default.ActorClass) != -1)
  107.                         bAddMe = EnableLoader();
  108.  
  109.         for (i = 0; i < default.RequiredIniEntries.Length; i++)
  110.                 if (!bAddMe && default.RequiredIniEntries[i].ClassFrom ~= "Engine.GameEngine" && default.RequiredIniEntries[i].PropName ~= "ServerActors")
  111.                         if (InStr(ServerActors,default.RequiredIniEntries[i].PropValue) != -1)
  112.                                 bAddMe = EnableLoader();
  113.  
  114.         if (default.DEBUG)
  115.         {
  116.                 log(default.Class@"Received string value:"$ServerActors,'CheckStrayActors');
  117.                 log(default.Class@"Returning"@bAddMe,'CheckStrayActors');
  118.         }
  119.         return bAddMe;
  120. }
  121.  
  122. // Only managed actors can be added to Ladder profiles
  123. // Normally, only the ActorClass of your loader (if bIncludeServerActor=True) would be need to be managed
  124. static function array<string> GetManagedActors()
  125. {
  126.         local int i;
  127.         local string A, B, C;
  128.         local array<string> ABC;
  129.  
  130.         i = -1;
  131.         while (static.AddManagedActor(i++,A,B,C))
  132.                 ABC[ABC.Length] = A$","$B$","$C;
  133.  
  134.         return ABC;
  135. }
  136.  
  137. // Managed actors will be passed FillPlayInfo() calls
  138. static function bool AddManagedActor(int Idx, out string ActorClassName, out string ActorName, out string ActorDesc)
  139. {
  140.         if (Idx == -1 && default.bIncludeServerActor)
  141.         {
  142.                 ActorClassName = default.ActorClass;
  143.                 ActorName = default.FriendlyName;
  144.                 ActorDesc = default.ActorDescription;
  145.                 return true;
  146.         }
  147.  
  148.         return false;
  149. }
  150.  
  151. // Optional hook for version filtering
  152. static function bool MatchesVersion(float ActorVersion, optional bool bExact, optional string NewURL)
  153. {
  154.         local float CurrentVersion;
  155.         local string LogText;
  156.  
  157.         if (default.RequiredVersion == "")
  158.                 return true;
  159.  
  160.         CurrentVersion = float(default.RequiredVersion);
  161.  
  162.         // Return false if loader version is higher than actor version
  163.         // Return false if loader version is lower than actor version and bExact=True
  164.         if ((ActorVersion < CurrentVersion) || (ActorVersion > CurrentVersion && bExact))
  165.         {
  166.                 if (default.VersionWarning != "")
  167.                 {
  168.                         LogText = static.ReplaceTag(default.VersionWarning,"%CurVer%",CurrentVersion);
  169.                         LogText = static.ReplaceTag(LogText,"%ReqVer%",ActorVersion);
  170.                         log(LogText);
  171.                 }
  172.  
  173.                 if (NewURL != "")
  174.                         log(static.ReplaceTag(default.DownloadMsg,"%URL%",NewURL));
  175.  
  176.                 return false;
  177.         }
  178.  
  179.         return true;
  180. }
  181.  
  182. // To prevent your loader from causing a server crash, add your checks here
  183. // return false to prevent your loader from being loaded
  184.  
  185. // You only need to override this function if bIncludeServerActor=False in your loader,
  186. // or if you have RequiredIniChanges that reference additional custom packages
  187. static function bool ValidateLoader()
  188. {
  189.         local class<Info>       MyActor;
  190.  
  191.         if (default.bIncludeServerActor && default.ActorClass != "")
  192.         {
  193.                 // Specify True for 3rd param in DynamicLoadObject to prevent log spam if MyActor isn't on server
  194.                 MyActor = class<Info>(DynamicLoadObject(default.ActorClass,class'Class',True));
  195.                 if (MyActor == None)
  196.                         return false;
  197.         }
  198.  
  199.         return true;
  200. }
  201.  
  202. // Hook for loader to cancel external removal request, or effect any specialized ini changes to make before removal
  203. function bool AcceptRemoval(optional array<Property> Props)
  204. {
  205.         if (Props.Length > 0)
  206.                 Properties = Props;
  207.  
  208.         RemoveMe();
  209.         return true;
  210. }
  211.  
  212. // should be subclassed
  213. // always use "return !bEnableMyLoader;"
  214. // see LadderLoader.LadderLoader or TeamBalanceLoader.BalanceLoader for examples
  215. function bool WantsToBeDisabled()
  216. {
  217.         return false;
  218. }
  219.  
  220. // called for each RequiredIniEntry
  221. // return true to apply the RequiredIniEntry.PropValue
  222. function bool ObjectNeedsUpdate(Object O, string PropName, string PropValue)
  223. {
  224.         local string Temp;
  225.  
  226.         Temp = O.GetPropertyText(PropName);
  227.         if ( InStr(Caps(Temp),Caps(PropValue)) < 0 )
  228.                 return true;
  229.  
  230.         return false;
  231. }
  232.  
  233. // notification of pending update - return false to skip update for loader
  234. function bool ApplyUpdate()
  235. {
  236.         if (DEBUG) log(class@"Returning"@IsActive(),'ApplyUpdate');
  237.         return IsActive();
  238. }
  239.  
  240. // return -1 if want to simply add entry
  241. // return index of array entry if want to overwrite
  242. function int CheckArrayEntry(string PropName, array<string> PropArray)
  243. {
  244.         return -1;
  245. }
  246.  
  247. // To maintain consistency, always add to "Server Actors" page
  248. // static function FillPlayInfo(PlayInfo PI)
  249. // {
  250. //              Super.FillPlayInfo(PI);
  251. //              PI.AddSetting("ServerActors",....
  252. // }
  253. //
  254. //###################################################################
  255. //###################################################################
  256. //
  257. //      Public Final Methods
  258. //      These are used to operate the internal mechanisms of the auto loader system.
  259. //
  260. //
  261.  
  262. // Called by ConfigMaster when Level.ServerTravel is called
  263.  
  264. // Removes loader if WantsToBeDisabled() returns True
  265. // Applies value for RequiredIniProperties if ObjectNeedsUpdate returns true
  266. final function UpdateConfiguration(array<Property> Props)
  267. {
  268.         local Object                    O;
  269.         local Property                  P;
  270.  
  271.         local string                    N,V;
  272.         local int                               i, j;
  273.  
  274.         local array<string>             Arr;
  275.         local string                    ArrS;
  276.  
  277.         if (DEBUG)
  278.                 log("Update configuration in"@class,'UpdateConfiguration');
  279.  
  280.         ResetConfig();
  281.         Properties = Props;
  282.  
  283.         if (WantsToBeDisabled())
  284.         {
  285.                 RemoveMe();
  286.                 return;
  287.         }
  288.  
  289.         if (bIncludeServerActor && ActorClass != "")
  290.         {
  291.                 O = GetObjectOfClass(class'Engine.GameEngine');
  292.                 if (O != None)
  293.                 {
  294.                         if (ObjectNeedsUpdate(O, "ServerActors", ActorClass))
  295.                         {
  296.                                 ArrS = O.GetPropertyText("ServerActors");
  297.                                 if (ArrS != "")
  298.                                         Arr = GenerateArray(ArrS);
  299.  
  300.                                 j = -1;
  301.                                 if (Arr.Length > 0)
  302.                                         j = CheckArrayEntry("ServerActors", Arr);
  303.  
  304.                                 if (j < 0)
  305.                                         O.SetPropertyText("ServerActors",AddDynArrayMember(O,"ServerActors",ActorClass));
  306.  
  307.                                 else O.SetPropertyText("ServerActors",InsertArrayMember(O, "ServerActors", ActorClass, j));
  308.                                 O.SaveConfig();
  309.                         }
  310.                 }
  311.         }
  312.  
  313.         for (i = 0;i<RequiredIniEntries.Length;i++)
  314.         {
  315.                 O = GetObjectForEntry(RequiredIniEntries[i]);
  316.                 if (O == None) continue;
  317.  
  318.                 if (!ObjectNeedsUpdate(O, RequiredIniEntries[i].PropName, RequiredIniEntries[i].PropValue)) continue;
  319.  
  320.                 N = RequiredIniEntries[i].PropName;
  321.                 V = RequiredIniEntries[i].PropValue;
  322.                 P = GetProperty(O, N);
  323.                 if (DEBUG)
  324.                         log(class@"got property"@p,'UpdateConfiguration');
  325.  
  326.                 if (P == None) continue;
  327.  
  328.                 j = -1;
  329.  
  330.                 if (PropIsArray(P))
  331.                 {
  332.                         ArrS = O.GetPropertyText(N);
  333.                         if (ArrS != "")
  334.                                 Arr = GenerateArray(ArrS);
  335.  
  336.                         if (Arr.Length > 0)
  337.                                 j = CheckArrayEntry(N, Arr);
  338.  
  339.                         if (j < 0)
  340.                                 O.SetPropertyText(N,AddDynArrayMember(O,N,V));
  341.  
  342.                         else O.SetPropertyText(N,InsertArrayMember(O, N, V, j));
  343.                 }
  344.  
  345.                 else
  346.                 {
  347.                         StoreDefaultValue(O, N);
  348.                         O.SetPropertyText(N,V);
  349.                 }
  350.  
  351.                 O.SaveConfig();
  352.         }
  353. }
  354.  
  355. // Regarding OriginalValues
  356. final function StoreDefaultValue(Object O, string PropName)
  357. {
  358.         local int i, idx;
  359.         local IniEntry NewDefault;
  360.         local string CurrentValue, Quote;
  361.         local array<string> Ar;
  362.  
  363.         if (DEBUG)
  364.                 log(class@"storing default"@o@propname,'StoreDefaultValue');
  365.  
  366.         for (i = 0; i < ConfigMaster(Owner).OriginalValues.Length; i++)
  367.                 if (ConfigMaster(Owner).OriginalValues[i].ClassFrom == string(O.Class) && ConfigMaster(Owner).OriginalValues[i].PropName == PropName)
  368.                         return;
  369.  
  370.         NewDefault.ClassFrom = string(O.Class);
  371.         // Check if this property name is a single array member
  372.         i = -1; idx = -1;
  373.         i = InStr(PropName, "§");
  374.         if (i != -1)
  375.         {
  376.                 idx = int(Left(PropName, i));
  377.                 PropName = Mid(PropName, i + 1);
  378.                 CurrentValue = O.GetPropertyText(PropName);
  379.                 Ar = GenerateArray(CurrentValue);
  380.                 CurrentValue = Ar[idx];
  381.  
  382.                 // Remove literal string wrapper
  383.                 if (Left(CurrentValue,1) == "\"")
  384.                 {
  385.                         CurrentValue = Mid(CurrentValue,1,Len(CurrentValue) - 2);
  386.                         Quote = "¶";
  387.                 }
  388.                 CurrentValue = idx $ "§" $ CurrentValue $ Quote;
  389.         }
  390.  
  391.         else CurrentValue = O.GetPropertyText(PropName);
  392.  
  393.         NewDefault.PropName = PropName;
  394.         NewDefault.PropValue = CurrentValue;
  395.         if (DEBUG)
  396.                 log(class@"old value:"@NewDefault.PropValue,'StoreDefaultValue');
  397.  
  398.         ConfigMaster(Owner).OriginalValues[ConfigMaster(Owner).OriginalValues.Length] = NewDefault;
  399.         ConfigMaster(Owner).SaveConfig();
  400. }
  401.  
  402. final function bool RestoreOriginalValue(Object O, string PropName)
  403. {
  404.         local int i, j, idx;
  405.         local string CurrentValue, StoredValue;
  406.         local array<string> Ar;
  407.  
  408.         for (i = 0; i < ConfigMaster(Owner).OriginalValues.Length; i++)
  409.         {
  410.                 if (ConfigMaster(Owner).OriginalValues[i].ClassFrom ~= string(O.Class) && ConfigMaster(Owner).OriginalValues[i].PropName ~= PropName)
  411.                 {
  412.                         StoredValue = ConfigMaster(Owner).OriginalValues[i].PropValue;
  413.                         // First check if this was a single array member
  414.                         j = InStr(StoredValue, "§");
  415.                         if (j != -1)
  416.                         {
  417.                                 idx = int(Left(StoredValue, j));
  418.                                 StoredValue = Mid(StoredValue, j + 1);
  419.                                 if (Right(StoredValue, 1) == "¶")
  420.                                 {
  421.                                         StoredValue = Left(StoredValue, Len(StoredValue) - 1);
  422.                                         StoredValue = "\"" $ StoredValue $ "\"";
  423.                                 }
  424.                                 CurrentValue = O.GetPropertyText(PropName);
  425.                                 Ar = GenerateArray(CurrentValue);
  426.                                 Ar[idx] = StoredValue;
  427.                                 StoredValue = "(" $ Join(Ar,",",True) $")";
  428.                         }
  429.  
  430.                         if (DEBUG)
  431.                                 log(class@"Assigning"@StoredValue@"to property"@string(O.Class)$"."$PropName,'RestoreOriginalValue');
  432.                         O.SetPropertyText(PropName, StoredValue);
  433.                         O.SaveConfig();
  434.                         break;
  435.                 }
  436.         }
  437.  
  438.         if (i < ConfigMaster(Owner).OriginalValues.Length)
  439.         {
  440.                 ConfigMaster(Owner).OriginalValues.Remove(i, 1);
  441.                 ConfigMaster(Owner).SaveConfig();
  442.                 return true;
  443.         }
  444.  
  445.         return false;
  446. }
  447.  
  448. //###################################################################
  449. //###################################################################
  450. //
  451. //      Internal Methods
  452. //      These are used to control the internal operation of the loader itself.
  453. //      These methods may only be called by other methods within the loader.
  454. //
  455.  
  456. protected final function RemoveMe()
  457. {
  458.         local int i;
  459.         local Object O;
  460.         local Property P;
  461.  
  462.         if (DEBUG)
  463.                 log(class@"being removed.",'RemoveMe');
  464.  
  465.         if (bIncludeServerActor && ActorClass != "")
  466.         {
  467.                 O = GetObjectOfClass(class'Engine.GameEngine');
  468.                 if (O != None)
  469.                         RemoveArrayEntry(O, "ServerActors", ActorClass);
  470.         }
  471.  
  472.         for (i = 0; i < RequiredIniEntries.Length; i++)
  473.         {
  474.                 O = GetObjectForEntry(RequiredIniEntries[i]);
  475.                 if (O == None) continue;
  476.  
  477.                 P = GetProperty(O,RequiredIniEntries[i].PropName);
  478.                 if (P != None && PropIsArray(P))
  479.                         RemoveArrayEntry(O,RequiredIniEntries[i].PropName,RequiredIniEntries[i].PropValue);
  480.  
  481.                 else RestoreOriginalValue(O, RequiredIniEntries[i].PropName);
  482.         }
  483.  
  484.         DisableLoader();
  485. }
  486.  
  487. protected final function string InsertArrayMember(Object Obj, string PropName, string NewValue, int Pos)
  488. {
  489.         local string CurValue, Quote, Tmp;
  490.         local array<string>     Members;
  491.         local bool bStatic;
  492.  
  493.         if (Obj == None)
  494.         {
  495.                 Warn("Object is None");
  496.                 return "";
  497.         }
  498.  
  499.         CurValue = Obj.GetPropertyText(PropName);
  500.         Members = GenerateArray(CurValue);
  501.  
  502.         if (DEBUG)
  503.         {
  504.                 log(class$":Inserting new member at position"@Pos@"to"@Obj$"."$PropName$":"@NewValue,'InsertArrayMember');
  505.                 log(class$":Current Value:"$CurValue,'InsertArrayMember');
  506.         }
  507.  
  508. // Check for literal string
  509.         if (InStr(Caps(CurValue), Caps(NewValue)) < 0 && Members.Length > 0)
  510.         {
  511.                 bStatic = Left(Members[pos], Len(PropName) + 1) ~= (PropName $ "[");
  512.                 Tmp = StringIf(bStatic, Mid(Members[pos],InStr(Members[pos],"=") + 1), Members[0]);
  513.                 Quote = StringIf(Left(Tmp,1) == "\"" && Left(NewValue,1) != "\"","\"","");
  514.  
  515.                 NewValue = StringIf(bStatic, PropName $ "=" $ Quote $ NewValue $ Quote, Quote $ NewValue $ Quote);
  516.                 if (DEBUG)
  517.                         log(InStr(Caps(CurValue),Caps(NewValue))@InStr(Caps(CurValue),Caps(NewValue))<0@Caps(CurValue)@"||"@Caps(NewValue),'AddDynArrayMember');
  518.  
  519.                 if (NewValue != "" && Pos > -1)
  520.                 {
  521.                         StoreDefaultValue(Obj, Pos$"§"$PropName);
  522.                         Members[Pos] = NewValue;
  523.                 }
  524.  
  525.                 CurValue = "(" $ Join(Members,",",True) $ ")";
  526.         }
  527.  
  528.         if (DEBUG)
  529.         {
  530.                 log(class@"Returning:"$CurValue,'InsertArrayMember');
  531.                 log(class@"",'InsertArrayMember');
  532.         }
  533.  
  534.         return CurValue;
  535. }
  536.  
  537. // Currently, UT2003 does not support setting static arrays through the use of SetPropertyText
  538. protected final function string AddStaticArrayMember(Object Obj, string PropName, string NewValue)
  539. {
  540.         local int i;
  541.         local array<string>     Members;
  542.         local string Quote, TempValue, CurValue;
  543.  
  544.         if (Obj == None)
  545.         {
  546.                 Warn("Object was None for property"@PropName);
  547.                 return "";
  548.         }
  549.  
  550.         CurValue = Obj.GetPropertyText(PropName);
  551.         Members = GenerateArray(CurValue);
  552.  
  553.         if (InStr(Caps(CurValue), Caps(NewValue)) < 0)
  554.         {
  555.                 if (Members.Length > 0)
  556.                 {
  557.                         TempValue = Mid(Members[0],InStr(Members[0],"=")+1);
  558.  
  559.                         // Check for literal string
  560.                         Quote = StringIf(Left(TempValue,1) == "\"" && Left(NewValue,1) != "\"", "\"", "");
  561.                         for (i = 0; i < Members.Length; i++)
  562.                         {
  563.                                 if (Members[i] == "")
  564.                                 {
  565.                                         Members[i] = PropName $ "[" $ i $ "]=" $ Quote $ NewValue $ Quote;
  566.                                         break;
  567.                                 }
  568.                         }
  569.                         CurValue = "(" $ Join(Members,",") $")";
  570.                 }
  571.  
  572.                 else CurValue = "(" $ PropName $ "[0]=" $ NewValue $ ")";
  573.         }
  574.  
  575.         return CurValue;
  576. }
  577.  
  578. protected final function string AddDynArrayMember(Object Obj, string PropName, string NewValue)
  579. {
  580.         local string CurValue, Quote;
  581.         local array<string>     Members;
  582.  
  583.         if (Obj == None)
  584.         {
  585.                 Warn("Object is None");
  586.                 return "";
  587.         }
  588.  
  589.         CurValue = Obj.GetPropertyText(PropName);
  590.         Members = GenerateArray(CurValue);
  591.         if (DEBUG)
  592.         {
  593.                 log(class$":Adding new member to"@Obj$"."$PropName$":"@NewValue,'AddDynArrayMember');
  594.                 log(class$":Current Value:"$CurValue,'AddDynArrayMember');
  595.         }
  596.  
  597. // Check for literal string
  598.         if (InStr(Caps(CurValue), Caps(NewValue)) < 0)
  599.         {
  600.                 if (Members.Length > 0)
  601.                 {
  602.                         if (DEBUG)
  603.                                 log(InStr(Caps(CurValue),Caps(NewValue))@InStr(Caps(CurValue),Caps(NewValue))<0@Caps(CurValue)@"||"@Caps(NewValue),'AddDynArrayMember');
  604.  
  605.                         Quote = StringIf(Left(Members[0],1) == "\"" && Left(NewValue,1) != "\"", "\"", "");
  606.                         Members[Members.Length] = Quote $ NewValue $ Quote;
  607.                         CurValue = "(" $ Join(Members,",",True) $ ")";
  608.                 }
  609.                 else CurValue = "(" $ NewValue $ ")";
  610.         }
  611.  
  612.         if (DEBUG)
  613.         {
  614.                 log(class@"Returning:"$CurValue,'AddDynArrayMember');
  615.                 log(class@"",'AddDynArrayMember');
  616.         }
  617.  
  618.         return CurValue;
  619. }
  620.  
  621. // UT2003 currently does not support setting the value of static arrays
  622. protected final function RemoveArrayEntry(Object O, string PropName, string PropValue)
  623. {
  624.         local int i, j;
  625.         local array<string> Members;
  626.         local string ArrayString, Quote, Tmp;
  627.         local bool bStatic;
  628.  
  629.         if (O == None)
  630.         {
  631.                 Warn("Object was none for property"@PropName);
  632.                 return;
  633.         }
  634.  
  635.         ArrayString = O.GetPropertyText(PropName);
  636.         if (ArrayString == "")
  637.         {
  638.                 Warn("Property was not found:"@PropName);
  639.                 return;
  640.         }
  641.  
  642.         if (DEBUG)
  643.         {
  644.                 log(class$": Removing array member"@string(O.Class)$"."$PropName$":"$ArrayString,'RemoveArrayEntry');
  645.                 log(class$": Member to be removed:"$PropValue,'RemoveArrayEntry');
  646.         }
  647.  
  648.         Members = GenerateArray(ArrayString);
  649.         bStatic = Left(Members[0], Len(PropName) + 1) ~= (PropName $ "[");
  650.         Tmp = StringIf(bStatic, Mid(Members[0],InStr(Members[0],"=") + 1), Members[0]);
  651.         Quote = StringIf(Left(Tmp,1) == "\"" && Left(PropValue,1) != "\"","\"","");
  652.  
  653.         PropValue = StringIf(bStatic, PropName $ "=" $ Quote $ PropValue $ Quote, Quote $ PropValue $ Quote);
  654.         for (i = 0; i < Members.Length; i++)
  655.         {
  656.                 if (DEBUG)
  657.                         log(class@"Comparing"@i@PropValue@"to"@Members[i],'RemoveArrayEntry');
  658.  
  659.                 if (Members[i] ~= PropValue)
  660.                         break;
  661.         }
  662.  
  663.         if (i < Members.Length)
  664.         {
  665.                 if (DEBUG) log(class@"Removing array member"@i$":"$Members[i],'RemoveArrayEntry');
  666.  
  667.                 // Check if we should restore a previous value
  668.                 for (j = 0; j < ConfigMaster(Owner).OriginalValues.Length; j++)
  669.                 {
  670.                         if (DEBUG)
  671.                         {
  672.                                 log(class@"Checking Backup Value"@j@"class:"$ConfigMaster(Owner).OriginalValues[j].ClassFrom@"against"@string(O.Class),'RemoveArrayEntry');
  673.                                 log(class@"Checking Backup Value"@j@"Property:"$ConfigMaster(Owner).OriginalValues[j].PropName@"against"@PropName,'RemoveArrayEntry');
  674.                         }
  675.  
  676.                         if (ConfigMaster(Owner).OriginalValues[j].ClassFrom ~= string(O.Class) &&
  677.                                 ConfigMaster(Owner).OriginalValues[j].PropName ~= PropName )
  678.                         {
  679.                                 RestoreOriginalValue(O, PropName);
  680.                                 break;
  681.                         }
  682.                 }
  683.  
  684.                 if (j < ConfigMaster(Owner).OriginalValues.Length)
  685.                         return;
  686.  
  687.                 Members.Remove(i,1);
  688.         }
  689.  
  690.         else return;
  691.  
  692.         PropValue = "(" $ Join(Members,",") $ ")";
  693.         if (DEBUG)
  694.                 log(class@"Assigning"@PropValue@"to property"@string(O.Class)$"."$PropName,'RemoveArrayEntry');
  695.         O.SetPropertyText(PropName,PropValue);
  696.         O.SaveConfig();
  697.         if (DEBUG)
  698.                 log(class@"Returning"@O.GetPropertyText(PropName),'RemoveArrayEntry');
  699. }
  700.  
  701. // Just a function to handle safe object creation
  702. protected final function Object GetObjectForEntry(IniEntry ThisEntry)
  703. {
  704.         local Object O;
  705.         local class<Object> OClass;
  706.  
  707.         if (DEBUG)
  708.                 log(class@"Getting"@ThisEntry.ClassFrom@ThisEntry.PropName@ThisEntry.PropValue,'GetObjectForEntry');
  709.  
  710.         OClass = class<Object>(DynamicLoadObject(ThisEntry.ClassFrom,class'Class'));
  711.         if (OClass == None)
  712.         {
  713.                 Warn("Could not load class"@ThisEntry.ClassFrom);
  714.