Mostly Harmless

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						Interaction${1}< ${3} >	// 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 Actor${1}< ${3} >
  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.Actor${1}< ${3} >
  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> O${1}< ${3} >
  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. 		return None;
  715. 	}
  716.  
  717. 	O = GetObjectOfClass(OClass);
  718. 	if (O == None)
  719. 		Warn("Unable to access object for class"@OClass);
  720.  
  721. 	return O;
  722. }
  723.  
  724. protected final function Object GetObjectOfClass(class<Object> ObjClass)
  725. {
  726. 	local Object Obj;
  727. 	local Actor A;
  728.  
  729. 	if (DEBUG)
  730. 		log(class@"Finding"@ObjClass,'GetObjectOfClass');
  731.  
  732. 	// Check for GameEngine
  733. 	if (ObjClass == class'GameEngine')
  734. 		return GE;
  735.  
  736. 	// First try the easy way
  737. 	foreach AllObjects(ObjClass,Obj)
  738. 		return Obj;
  739.  
  740. 	// Object may not be loaded right now, so attempt to load it
  741. 	// If actor, spawn it
  742. 	if (ClassIsChildOf(ObjClass,class'Actor'))
  743. 	{
  744. 		A = Spawn(class<Actor>(ObjClass));
  745. 		A.GoToState('');
  746. 		return A;
  747. 	}
  748.  
  749. 	// if object, new it
  750. 	Obj = new(None) Obj${1}< ${3} >
  751.  
  752. 	// might be property
  753. 	if (Obj == None)
  754. 		Obj = new(Class) Obj${1}< ${3} >
  755.  
  756. 	// Don't know if this will even work ?
  757. 	if (Obj == None)
  758. 		Obj = New(ObjClass.default.Outer) Obj${1}< ${3} >
  759.  
  760. 	if (DEBUG)
  761. 	{
  762. 		log(class@"Could not create new object"@ObjClass,'GetObjectOfClass');
  763. 		log(class@ObjClass@"outer is"@ObjClass.default.Outer,'GetObjectOfClass');
  764. 		log(class@"",'GetObjectOfClass');
  765. 	}
  766.  
  767. 	return Obj;
  768. }
  769.  
  770. protected final function Property GetProperty(Object O, string PropName)
  771. {
  772. 	local int i;
  773. 	local Class ClassOuter;
  774. 	local bool classexact, classchild, propn, seen${1}< ${3} >
  775.  
  776. 	for (i=0;i<Properties.Length;i++)
  777. 	{
  778. 		seenclass = Class(Properties[i].Outer) == ClassOuter;
  779.  
  780. 		ClassOuter = Class(Properties[i].Outer);
  781. 		if (DEBUGPROPS)
  782. 		{
  783. 			if (ClassOuter == None)
  784. 			{
  785. 				log(class@"Property Outer is not a class!",'GetProperty');
  786. 				continue;
  787. 			}
  788.  
  789. 			if (!seenclass)
  790. 			{
  791. 				log(class@"Compare Obj Class"@O.Class@"to"@Properties[i].Outer,'GetProperty');
  792. 				if (O.Class==Properties[i].Outer)
  793. 					classexact = true;
  794. 			}
  795.  
  796. 			if (!classchild && ClassIsChildOf(O.Class,ClassOuter))
  797. 			{
  798. 				log(class@"Obj class"@O.Class@"is child of"@ClassOuter,'GetProperty');
  799. 				classchild = true;
  800. 			}
  801.  
  802. 			else if (!classchild && ClassIsChildOf(ClassOuter,O.Class))
  803. 			{
  804. 				log(class@"Obj class"@O.Class@"is parent of"@ClassOuter,'GetProperty');
  805. 				classchild = true;
  806. 			}
  807.  
  808. 			if (!propn && classexact)
  809. 			{
  810. 				log(class@"Compare Name"@PropName@"to"@Properties[i].Name,'GetProperty');
  811. 				if (PropName == string(Properties[i].Name))
  812. 					propn = true;
  813. 			}
  814. 		}
  815.  
  816. 		if ((ClassIsChildOf(O.Class,ClassOuter)||O.Class == Properties[i].Outer||ClassIsChildOf(ClassOuter,O.Class)) && string(Properties[i].Name) == PropName)
  817. 		{
  818. 			if (DEBUG)
  819. 			{
  820. 				log(class@"Returning Property"@Properties[i],'GetProperty');
  821. 				log(class@"",'GetProperty');
  822. 			}
  823.  
  824. 			return Properties[i];
  825. 		}
  826. 	}
  827.  
  828. 	return None;
  829. }
  830.  
  831. protected final function bool PropIsArray(Property P)
  832. {
  833. 	if (DEBUG)
  834. 		log(class@"Checking Property"@P.Outer$"."$P.Name$":"@P.Class,'PropIsArray');
  835. 	return P.Class == class'ArrayProperty';
  836. }
  837.  
  838. protected static final function array<string> GenerateArray(string ArrayString)
  839. {
  840. 	local array<string>	Members;
  841. 	local int i;
  842. 	local string S;
  843.  
  844. 	if (ArrayString != "")
  845. 	{
  846. 		// Remove the array wrapper ( )
  847. 		ArrayString = Mid(ArrayString,1,Len(ArrayString)-2);
  848.  
  849. 		// If string contains internal containers, then have to split differently
  850. 		if (Left(ArrayString,1) == "(")
  851. 		{
  852. 			do {
  853. 				do {
  854. 					if (Left(ArrayString,1) == ")")
  855. 						i--;
  856. 					else if (Left(ArrayString,1) == "(")
  857. 						i++;
  858. 					Eat(S, ArrayString, 1);
  859. 				} until (i == 0);
  860.  
  861. 				Members[Members.Length] = S;
  862. 				S = "";
  863. 				if (ArrayString != "" && Left(ArrayString,1) == ",")
  864. 					ArrayString = Mid(ArrayString,1);
  865. 			} until (ArrayString == "");
  866. 		}
  867.  
  868. 		else Split2(ArrayString,",",Members);
  869. 	}
  870.  
  871. 	return Members;
  872. }
  873.  
  874. protected static final function bool NotInPlayInfo(PlayInfo PI, class<Info> NewInfo)
  875. {
  876. 	local int i;
  877. 	if (PI == None)
  878. 	{
  879. 		Warn("Invalid PlayInfo Object!");
  880. 		return false;
  881. 	}
  882.  
  883. 	for (i=0;i<PI.InfoClasses.Length;i++)
  884. 	{
  885. 		if (PI.InfoClasses[i] == NewInfo)
  886. 			return false;
  887. 	}
  888.  
  889. 	return true;
  890. }
  891.  
  892.  
  893. //###################################################################
  894. //###################################################################
  895. //
  896. //	Utility Methods
  897. //	These are used perform various tedious operations.
  898. //
  899. // Moves Num elements from Source to Dest
  900. static final function Eat(out string Dest, out string Source, int Num)
  901. {
  902. 	Dest = Dest $ Left(Source, Num);
  903. 	Source = Mid(Source, Num);
  904. }
  905.  
  906. static final function string StringIf(bool Condition, string IfTrue, string IfFalse)
  907. {
  908. 	if (Condition) return IfTrue;
  909. 	return IfFalse;
  910. }
  911.  
  912. // Based on AccessControlIni & Object.ReplaceText()
  913. static final function string ReplaceTag(string from, string tag, coerce string with)
  914. {
  915. 	local int i;
  916. 	local string t;
  917.  
  918. 	// InStr() is case-sensitive
  919. 	i = InStr(Caps(from), Caps(tag));
  920. 	while (i != -1)
  921. 	{
  922. 		t = t $ Left(from,i) $ with;
  923. 		from = mid(from,i+len(tag));
  924. 		i = InStr(Caps(from), Caps(tag));
  925. 	}
  926.  
  927. 	t = t $ from;
  928. 	return t;
  929. }
  930.  
  931. // Following functions from wUtils103 by El_Muerte[TDS]
  932. // Included by permission (copied to avoid package dependancy)
  933.  
  934. // Shifts an element off a string
  935. // example (delim = ' '): 'this is a string' -> 'is a string'
  936. // if quotechar = " : '"this is" a string' -> 'a string'
  937. static final function string StrShift(out string line, string delim, optional string quotechar)
  938. {
  939.     local int delimpos, quotepos;
  940.     local string result;
  941.  
  942.     if ( quotechar != "" && Left(line, Len(quotechar)) == quotechar ) {
  943.         do {
  944.             quotepos = InstrFrom(line, quotechar, quotepos + 1);
  945.         } until (quotepos == -1 || quotepos + Len(quotechar) == Len(line)
  946.                 || Mid(line, quotepos + len(quotechar), len(delim)) == delim);
  947.     }
  948.     if ( quotepos != -1 ) {
  949.         delimpos = InstrFrom(line, delim, quotepos);
  950.     }
  951.     else {
  952.         delimpos = Instr(line, delim);
  953.     }
  954.  
  955.     if (delimpos == -1)
  956.     {
  957.         result = line;
  958.         line = "";
  959.     }
  960.     else {
  961.         result = Left(line,delimpos);
  962.         line = Mid(line,delimpos+len(delim));
  963.     }
  964.     if ( quotechar != "" && Left(result, Len(quotechar)) == quotechar ) {
  965.       result = Mid(result, Len(quotechar), Len(result)-(Len(quotechar)*2));
  966.     }
  967.     return result;
  968. }
  969.  
  970. // Join the elements of a string array to an array
  971. static final function string Join(array< string > ar, optional string delim, optional bool bIgnoreEmpty)
  972. {
  973.   local string result;
  974.   local int i;
  975.   for (i = 0; i < ar.length; i++)
  976.   {
  977.     if (bIgnoreEmpty && ar[i] == "") continue;
  978.     if (result != "") result = result$delim;
  979.     result = result$ar[i];
  980.   }
  981.  
  982.   return result;
  983. }
  984.  
  985. // Fixed split method
  986. // no problems when it starts with a delim
  987. // no problems with ending spaces
  988. // delim can be a string
  989. static final function int Split2(coerce string src, string delim, out array<string> parts, optional bool ignoreEmpty, optional string quotechar)
  990. {
  991.   local string temp;
  992.   Parts.Remove(0, Parts.Length);
  993.   if (delim == "" || Src == "" ) return 0;
  994.   while (src != "")
  995.   {
  996.     temp = StrShift(src, delim, quotechar);
  997.     if (temp == "")
  998.     {
  999.       if (!ignoreEmpty)
  1000.       {
  1001.         parts.length = parts.length+1;
  1002.         parts[parts.length-1] = temp;
  1003.       }
  1004.     }
  1005.     else {
  1006.       parts.length = parts.length+1;
  1007.       parts[parts.length-1] = temp;
  1008.     }
  1009.   }
  1010.   return parts.length;
  1011. }
  1012.  
  1013. // InStr starting from an offset
  1014. static final function int InStrFrom(coerce string StrText, coerce string StrPart, optional int OffsetStart)
  1015. {
  1016.   local int OffsetPart;
  1017.  
  1018.   OffsetPart = InStr(Mid(StrText, OffsetStart), StrPart);
  1019.   if (OffsetPart >= 0)
  1020.     OffsetPart += OffsetStart;
  1021.   return OffsetPart;
  1022. }

Wormbo: Hmm, a little documentation instead of the source code wouldn't hurt. E.g. how can you use this to enable/disable a server actor?

El Muerte TDS: like this: ChatFilter AutoLoader, maybe write a [/example] based on it... first lunch