Legacy:AutoLoader

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to navigation Jump to search
UT2003 :: Actor >> Info >> AutoLoader (Package: ConfigManager)

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

<uscript line> //----------------------------------------------------------- // ConfigManager.AutoLoader // // This class has two functions: // 1. Perform all required ini changes needed for your mod. Will also UNDO // any changes made to the ini if your mod is deactivated. // Additional benefit of auto-installation of your mutator on a dedicated // server (to enable set bEnableMyLoader=True in defaultproperties) // // 2. Pass FillPlayInfo() calls to mutators which are not part of the game's // mutator list (such as mutators which only require ServerActors= lines in ini) // //----------------------------------------------------------- class AutoLoader extends Info abstract;

// Quick note about debugging - By setting DEBUG=True in defaultproperties of your // AutoLoader subclass when compiling, you can receive large amounts of debug data. // However, you will quickly encounter the 1024 byte limit if you use ucc.exe to // start the testing server. By starting a server using the "ut2003 -server" command, // you will bypass this limit, since ut2003.exe does not have these limitations.

var const bool DEBUG; var const bool DEBUGPROPS;

var ConfigMaster Manager; // Pointer to ConfigMaster mutator var string InteractionClass; // Not yet implemented

//======================================= // Loading ServerActors //======================================= // Pointer to GameEngine var GameEngine GE;

// This loader includes a server-side only mutator or server actor // that will not be part of the mutator chain (will receive FillPlayInfo() calls) var() bool bIncludeServerActor;

// Classname of the ServerActor this loader loads // Same value as what would otherwise be your mod's // ServerActors= line in the .ini file. var() string ActorClass;

// Friendly Name of the Server Actor class // Used by webadmin/adminmenu as the name of the server actor var() localized string FriendlyName; var() localized string ActorDescription;

// Lock this loader to a certain version (or higher) of your mod var() string RequiredVersion; // Required version of actor var() localized string VersionWarning; // Message to write to server log if version not enough var() localized string DownloadMsg; // Message to write to log

//======================================= // Automatic configuration changes //======================================= // A single ini change struct IniEntry { var() string ClassFrom; // Class which contains the setting we want to change var() string PropName; // Name of the variable we're trying to change var() string PropValue; // Value to apply };

// Array of ini settings required by this mod var() array<IniEntry> RequiredIniEntries; var array<Property> Properties;


//################################################################### //################################################################### // // Public methods - should be subclassed to customize loader's response

// if return true, loader class will be spawned static function bool IsActive() { return false; }

// should be subclassed - set loader to active/will be included next match static function bool EnableLoader(optional string SpecialActor) { return false; }

// should be subclassed static function bool DisableLoader(optional string SpecialActor) { return true; }

// called on all loader classes - used to activate loader based on active mutators static function bool CheckCurrentMutators(string URL) { return false; }

// called on all loader classes which have bIncludeServerActor=True - used to activate loader based on manual ServerActor entry static function bool CheckStrayActors(string ServerActors) { local int i; local bool bAddMe;

if (default.bIncludeServerActor) if (InStr(ServerActors,default.ActorClass) != -1) bAddMe = EnableLoader();

for (i = 0; i < default.RequiredIniEntries.Length; i++) if (!bAddMe && default.RequiredIniEntries[i].ClassFrom ~= "Engine.GameEngine" && default.RequiredIniEntries[i].PropName ~= "ServerActors") if (InStr(ServerActors,default.RequiredIniEntries[i].PropValue) != -1) bAddMe = EnableLoader();

if (default.DEBUG) { log(default.Class@"Received string value:"$ServerActors,'CheckStrayActors'); log(default.Class@"Returning"@bAddMe,'CheckStrayActors'); } return bAddMe; }

// Only managed actors can be added to Ladder profiles // Normally, only the ActorClass of your loader (if bIncludeServerActor=True) would be need to be managed static function array<string> GetManagedActors() { local int i; local string A, B, C; local array<string> ABC;

i = -1; while (static.AddManagedActor(i++,A,B,C)) ABC[ABC.Length] = A$","$B$","$C;

return ABC; }

// Managed actors will be passed FillPlayInfo() calls static function bool AddManagedActor(int Idx, out string ActorClassName, out string ActorName, out string ActorDesc) { if (Idx == -1 && default.bIncludeServerActor) { ActorClassName = default.ActorClass; ActorName = default.FriendlyName; ActorDesc = default.ActorDescription; return true; }

return false; }

// Optional hook for version filtering static function bool MatchesVersion(float ActorVersion, optional bool bExact, optional string NewURL) { local float CurrentVersion; local string LogText;

if (default.RequiredVersion == "") return true;

CurrentVersion = float(default.RequiredVersion);

// Return false if loader version is higher than actor version // Return false if loader version is lower than actor version and bExact=True if ((ActorVersion < CurrentVersion) || (ActorVersion > CurrentVersion && bExact)) { if (default.VersionWarning != "") { LogText = static.ReplaceTag(default.VersionWarning,"%CurVer%",CurrentVersion); LogText = static.ReplaceTag(LogText,"%ReqVer%",ActorVersion); log(LogText); }

if (NewURL != "") log(static.ReplaceTag(default.DownloadMsg,"%URL%",NewURL));

return false; }

return true; }

// To prevent your loader from causing a server crash, add your checks here // return false to prevent your loader from being loaded

// You only need to override this function if bIncludeServerActor=False in your loader, // or if you have RequiredIniChanges that reference additional custom packages static function bool ValidateLoader() { local class<Info> MyActor;

if (default.bIncludeServerActor && default.ActorClass != "") { // Specify True for 3rd param in DynamicLoadObject to prevent log spam if MyActor isn't on server MyActor = class<Info>(DynamicLoadObject(default.ActorClass,class'Class',True)); if (MyActor == None) return false; }

return true; }

// Hook for loader to cancel external removal request, or effect any specialized ini changes to make before removal function bool AcceptRemoval(optional array<Property> Props) { if (Props.Length > 0) Properties = Props;

RemoveMe(); return true; }

// should be subclassed // always use "return !bEnableMyLoader;" // see LadderLoader.LadderLoader or TeamBalanceLoader.BalanceLoader for examples function bool WantsToBeDisabled() { return false; }

// called for each RequiredIniEntry // return true to apply the RequiredIniEntry.PropValue function bool ObjectNeedsUpdate(Object O, string PropName, string PropValue) { local string Temp;

Temp = O.GetPropertyText(PropName); if ( InStr(Caps(Temp),Caps(PropValue)) < 0 ) return true;

return false; }

// notification of pending update - return false to skip update for loader function bool ApplyUpdate() { if (DEBUG) log(class@"Returning"@IsActive(),'ApplyUpdate'); return IsActive(); }

// return -1 if want to simply add entry // return index of array entry if want to overwrite function int CheckArrayEntry(string PropName, array<string> PropArray) { return -1; }

// To maintain consistency, always add to "Server Actors" page // static function FillPlayInfo(PlayInfo PI) // { // Super.FillPlayInfo(PI); // PI.AddSetting("ServerActors",.... // } // //################################################################### //################################################################### // // Public Final Methods // These are used to operate the internal mechanisms of the auto loader system. // //

// Called by ConfigMaster when Level.ServerTravel is called

// Removes loader if WantsToBeDisabled() returns True // Applies value for RequiredIniProperties if ObjectNeedsUpdate returns true final function UpdateConfiguration(array<Property> Props) { local Object O; local Property P;

local string N,V; local int i, j;

local array<string> Arr; local string ArrS;

if (DEBUG) log("Update configuration in"@class,'UpdateConfiguration');

ResetConfig(); Properties = Props;

if (WantsToBeDisabled()) { RemoveMe(); return; }

if (bIncludeServerActor && ActorClass != "") { O = GetObjectOfClass(class'Engine.GameEngine'); if (O != None) { if (ObjectNeedsUpdate(O, "ServerActors", ActorClass)) { ArrS = O.GetPropertyText("ServerActors"); if (ArrS != "") Arr = GenerateArray(ArrS);

j = -1; if (Arr.Length > 0) j = CheckArrayEntry("ServerActors", Arr);

if (j < 0) O.SetPropertyText("ServerActors",AddDynArrayMember(O,"ServerActors",ActorClass));

else O.SetPropertyText("ServerActors",InsertArrayMember(O, "ServerActors", ActorClass, j)); O.SaveConfig(); } } }

for (i = 0;i<RequiredIniEntries.Length;i++) { O = GetObjectForEntry(RequiredIniEntries[i]); if (O == None) continue;

if (!ObjectNeedsUpdate(O, RequiredIniEntries[i].PropName, RequiredIniEntries[i].PropValue)) continue;

N = RequiredIniEntries[i].PropName; V = RequiredIniEntries[i].PropValue; P = GetProperty(O, N); if (DEBUG) log(class@"got property"@p,'UpdateConfiguration');

if (P == None) continue;

j = -1;

if (PropIsArray(P)) { ArrS = O.GetPropertyText(N); if (ArrS != "") Arr = GenerateArray(ArrS);

if (Arr.Length > 0) j = CheckArrayEntry(N, Arr);

if (j < 0) O.SetPropertyText(N,AddDynArrayMember(O,N,V));

else O.SetPropertyText(N,InsertArrayMember(O, N, V, j)); }

else { StoreDefaultValue(O, N); O.SetPropertyText(N,V); }

O.SaveConfig(); } }

// Regarding OriginalValues final function StoreDefaultValue(Object O, string PropName) { local int i, idx; local IniEntry NewDefault; local string CurrentValue, Quote; local array<string> Ar;

if (DEBUG) log(class@"storing default"@o@propname,'StoreDefaultValue');

for (i = 0; i < ConfigMaster(Owner).OriginalValues.Length; i++) if (ConfigMaster(Owner).OriginalValues[i].ClassFrom == string(O.Class) && ConfigMaster(Owner).OriginalValues[i].PropName == PropName) return;

NewDefault.ClassFrom = string(O.Class); // Check if this property name is a single array member i = -1; idx = -1; i = InStr(PropName, "§"); if (i != -1) { idx = int(Left(PropName, i)); PropName = Mid(PropName, i + 1); CurrentValue = O.GetPropertyText(PropName); Ar = GenerateArray(CurrentValue); CurrentValue = Ar[idx];

// Remove literal string wrapper if (Left(CurrentValue,1) == "\"") { CurrentValue = Mid(CurrentValue,1,Len(CurrentValue) - 2); Quote = "¶"; } CurrentValue = idx $ "§" $ CurrentValue $ Quote; }

else CurrentValue = O.GetPropertyText(PropName);

NewDefault.PropName = PropName; NewDefault.PropValue = CurrentValue; if (DEBUG) log(class@"old value:"@NewDefault.PropValue,'StoreDefaultValue');

ConfigMaster(Owner).OriginalValues[ConfigMaster(Owner).OriginalValues.Length] = NewDefault; ConfigMaster(Owner).SaveConfig(); }

final function bool RestoreOriginalValue(Object O, string PropName) { local int i, j, idx; local string CurrentValue, StoredValue; local array<string> Ar;

for (i = 0; i < ConfigMaster(Owner).OriginalValues.Length; i++) { if (ConfigMaster(Owner).OriginalValues[i].ClassFrom ~= string(O.Class) && ConfigMaster(Owner).OriginalValues[i].PropName ~= PropName) { StoredValue = ConfigMaster(Owner).OriginalValues[i].PropValue; // First check if this was a single array member j = InStr(StoredValue, "§"); if (j != -1) { idx = int(Left(StoredValue, j)); StoredValue = Mid(StoredValue, j + 1); if (Right(StoredValue, 1) == "¶") { StoredValue = Left(StoredValue, Len(StoredValue) - 1); StoredValue = "\"" $ StoredValue $ "\""; } CurrentValue = O.GetPropertyText(PropName); Ar = GenerateArray(CurrentValue); Ar[idx] = StoredValue; StoredValue = "(" $ Join(Ar,",",True) $")"; }

if (DEBUG) log(class@"Assigning"@StoredValue@"to property"@string(O.Class)$"."$PropName,'RestoreOriginalValue'); O.SetPropertyText(PropName, StoredValue); O.SaveConfig(); break; } }

if (i < ConfigMaster(Owner).OriginalValues.Length) { ConfigMaster(Owner).OriginalValues.Remove(i, 1); ConfigMaster(Owner).SaveConfig(); return true; }

return false; }

//################################################################### //################################################################### // // Internal Methods // These are used to control the internal operation of the loader itself. // These methods may only be called by other methods within the loader. //

protected final function RemoveMe() { local int i; local Object O; local Property P;

if (DEBUG) log(class@"being removed.",'RemoveMe');

if (bIncludeServerActor && ActorClass != "") { O = GetObjectOfClass(class'Engine.GameEngine'); if (O != None) RemoveArrayEntry(O, "ServerActors", ActorClass); }

for (i = 0; i < RequiredIniEntries.Length; i++) { O = GetObjectForEntry(RequiredIniEntries[i]); if (O == None) continue;

P = GetProperty(O,RequiredIniEntries[i].PropName); if (P != None && PropIsArray(P)) RemoveArrayEntry(O,RequiredIniEntries[i].PropName,RequiredIniEntries[i].PropValue);

else RestoreOriginalValue(O, RequiredIniEntries[i].PropName); }

DisableLoader(); }

protected final function string InsertArrayMember(Object Obj, string PropName, string NewValue, int Pos) { local string CurValue, Quote, Tmp; local array<string> Members; local bool bStatic;

if (Obj == None) { Warn("Object is None"); return ""; }

CurValue = Obj.GetPropertyText(PropName); Members = GenerateArray(CurValue);

if (DEBUG) { log(class$":Inserting new member at position"@Pos@"to"@Obj$"."$PropName$":"@NewValue,'InsertArrayMember'); log(class$":Current Value:"$CurValue,'InsertArrayMember'); }

// Check for literal string if (InStr(Caps(CurValue), Caps(NewValue)) < 0 && Members.Length > 0) { bStatic = Left(Members[pos], Len(PropName) + 1) ~= (PropName $ "["); Tmp = StringIf(bStatic, Mid(Members[pos],InStr(Members[pos],"=") + 1), Members[0]); Quote = StringIf(Left(Tmp,1) == "\"" && Left(NewValue,1) != "\"","\"","");

NewValue = StringIf(bStatic, PropName $ "=" $ Quote $ NewValue $ Quote, Quote $ NewValue $ Quote); if (DEBUG) log(InStr(Caps(CurValue),Caps(NewValue))@InStr(Caps(CurValue),Caps(NewValue))<0@Caps(CurValue)@"||"@Caps(NewValue),'AddDynArrayMember');

if (NewValue != "" && Pos > -1) { StoreDefaultValue(Obj, Pos$"§"$PropName); Members[Pos] = NewValue; }

CurValue = "(" $ Join(Members,",",True) $ ")"; }

if (DEBUG) { log(class@"Returning:"$CurValue,'InsertArrayMember'); log(class@"",'InsertArrayMember'); }

return CurValue; }

// Currently, UT2003 does not support setting static arrays through the use of SetPropertyText protected final function string AddStaticArrayMember(Object Obj, string PropName, string NewValue) { local int i; local array<string> Members; local string Quote, TempValue, CurValue;

if (Obj == None) { Warn("Object was None for property"@PropName); return ""; }

CurValue = Obj.GetPropertyText(PropName); Members = GenerateArray(CurValue);

if (InStr(Caps(CurValue), Caps(NewValue)) < 0) { if (Members.Length > 0) { TempValue = Mid(Members[0],InStr(Members[0],"=")+1);

// Check for literal string Quote = StringIf(Left(TempValue,1) == "\"" && Left(NewValue,1) != "\"", "\"", ""); for (i = 0; i < Members.Length; i++) { if (Members[i] == "") { Members[i] = PropName $ "[" $ i $ "]=" $ Quote $ NewValue $ Quote; break; } } CurValue = "(" $ Join(Members,",") $")"; }

else CurValue = "(" $ PropName $ "[0]=" $ NewValue $ ")"; }

return CurValue; }

protected final function string AddDynArrayMember(Object Obj, string PropName, string NewValue) { local string CurValue, Quote; local array<string> Members;

if (Obj == None) { Warn("Object is None"); return ""; }

CurValue = Obj.GetPropertyText(PropName); Members = GenerateArray(CurValue); if (DEBUG) { log(class$":Adding new member to"@Obj$"."$PropName$":"@NewValue,'AddDynArrayMember'); log(class$":Current Value:"$CurValue,'AddDynArrayMember'); }

// Check for literal string if (InStr(Caps(CurValue), Caps(NewValue)) < 0) { if (Members.Length > 0) { if (DEBUG) log(InStr(Caps(CurValue),Caps(NewValue))@InStr(Caps(CurValue),Caps(NewValue))<0@Caps(CurValue)@"||"@Caps(NewValue),'AddDynArrayMember');

Quote = StringIf(Left(Members[0],1) == "\"" && Left(NewValue,1) != "\"", "\"", ""); Members[Members.Length] = Quote $ NewValue $ Quote; CurValue = "(" $ Join(Members,",",True) $ ")"; } else CurValue = "(" $ NewValue $ ")"; }

if (DEBUG) { log(class@"Returning:"$CurValue,'AddDynArrayMember'); log(class@"",'AddDynArrayMember'); }

return CurValue; }

// UT2003 currently does not support setting the value of static arrays protected final function RemoveArrayEntry(Object O, string PropName, string PropValue) { local int i, j; local array<string> Members; local string ArrayString, Quote, Tmp; local bool bStatic;

if (O == None) { Warn("Object was none for property"@PropName); return; }

ArrayString = O.GetPropertyText(PropName); if (ArrayString == "") { Warn("Property was not found:"@PropName); return; }

if (DEBUG) { log(class$": Removing array member"@string(O.Class)$"."$PropName$":"$ArrayString,'RemoveArrayEntry'); log(class$": Member to be removed:"$PropValue,'RemoveArrayEntry'); }

Members = GenerateArray(ArrayString); bStatic = Left(Members[0], Len(PropName) + 1) ~= (PropName $ "["); Tmp = StringIf(bStatic, Mid(Members[0],InStr(Members[0],"=") + 1), Members[0]); Quote = StringIf(Left(Tmp,1) == "\"" && Left(PropValue,1) != "\"","\"","");

PropValue = StringIf(bStatic, PropName $ "=" $ Quote $ PropValue $ Quote, Quote $ PropValue $ Quote); for (i = 0; i < Members.Length; i++) { if (DEBUG) log(class@"Comparing"@i@PropValue@"to"@Members[i],'RemoveArrayEntry');

if (Members[i] ~= PropValue) break; }

if (i < Members.Length) { if (DEBUG) log(class@"Removing array member"@i$":"$Members[i],'RemoveArrayEntry');

// Check if we should restore a previous value for (j = 0; j < ConfigMaster(Owner).OriginalValues.Length; j++) { if (DEBUG) { log(class@"Checking Backup Value"@j@"class:"$ConfigMaster(Owner).OriginalValues[j].ClassFrom@"against"@string(O.Class),'RemoveArrayEntry'); log(class@"Checking Backup Value"@j@"Property:"$ConfigMaster(Owner).OriginalValues[j].PropName@"against"@PropName,'RemoveArrayEntry'); }

if (ConfigMaster(Owner).OriginalValues[j].ClassFrom ~= string(O.Class) && ConfigMaster(Owner).OriginalValues[j].PropName ~= PropName ) { RestoreOriginalValue(O, PropName); break; } }

if (j < ConfigMaster(Owner).OriginalValues.Length) return;

Members.Remove(i,1); }

else return;

PropValue = "(" $ Join(Members,",") $ ")"; if (DEBUG) log(class@"Assigning"@PropValue@"to property"@string(O.Class)$"."$PropName,'RemoveArrayEntry'); O.SetPropertyText(PropName,PropValue); O.SaveConfig(); if (DEBUG) log(class@"Returning"@O.GetPropertyText(PropName),'RemoveArrayEntry'); }

// Just a function to handle safe object creation protected final function Object GetObjectForEntry(IniEntry ThisEntry) { local Object O; local class<Object> OClass;

if (DEBUG) log(class@"Getting"@[email protected]@ThisEntry.PropValue,'GetObjectForEntry');

OClass = class<Object>(DynamicLoadObject(ThisEntry.ClassFrom,class'Class')); if (OClass == None) { Warn("Could not load class"@ThisEntry.ClassFrom); return None; }

O = GetObjectOfClass(OClass); if (O == None) Warn("Unable to access object for class"@OClass);

return O; }

protected final function Object GetObjectOfClass(class<Object> ObjClass) { local Object Obj; local Actor A;

if (DEBUG) log(class@"Finding"@ObjClass,'GetObjectOfClass');

// Check for GameEngine if (ObjClass == class'GameEngine') return GE;

// First try the easy way foreach AllObjects(ObjClass,Obj) return Obj;

// Object may not be loaded right now, so attempt to load it // If actor, spawn it if (ClassIsChildOf(ObjClass,class'Actor')) { A = Spawn(class<Actor>(ObjClass)); A.GoToState(); return A; }

// if object, new it Obj = new(None) ObjClass;

// might be property if (Obj == None) Obj = new(Class) ObjClass;

// Don't know if this will even work ? if (Obj == None) Obj = New(ObjClass.default.Outer) ObjClass;

if (DEBUG) { log(class@"Could not create new object"@ObjClass,'GetObjectOfClass'); log(class@ObjClass@"outer is"@ObjClass.default.Outer,'GetObjectOfClass'); log(class@"",'GetObjectOfClass'); }

return Obj; }

protected final function Property GetProperty(Object O, string PropName) { local int i; local Class ClassOuter; local bool classexact, classchild, propn, seenclass;

for (i=0;i<Properties.Length;i++) { seenclass = Class(Properties[i].Outer) == ClassOuter;

ClassOuter = Class(Properties[i].Outer); if (DEBUGPROPS) { if (ClassOuter == None) { log(class@"Property Outer is not a class!",'GetProperty'); continue; }

if (!seenclass) { log(class@"Compare Obj Class"@O.Class@"to"@Properties[i].Outer,'GetProperty'); if (O.Class==Properties[i].Outer) classexact = true; }

if (!classchild && ClassIsChildOf(O.Class,ClassOuter)) { log(class@"Obj class"@O.Class@"is child of"@ClassOuter,'GetProperty'); classchild = true; }

else if (!classchild && ClassIsChildOf(ClassOuter,O.Class)) { log(class@"Obj class"@O.Class@"is parent of"@ClassOuter,'GetProperty'); classchild = true; }

if (!propn && classexact) { log(class@"Compare Name"@PropName@"to"@Properties[i].Name,'GetProperty'); if (PropName == string(Properties[i].Name)) propn = true; } }

if ((ClassIsChildOf(O.Class,ClassOuter)||O.Class == Properties[i].Outer||ClassIsChildOf(ClassOuter,O.Class)) && string(Properties[i].Name) == PropName) { if (DEBUG) { log(class@"Returning Property"@Properties[i],'GetProperty'); log(class@"",'GetProperty'); }

return Properties[i]; } }

return None; }

protected final function bool PropIsArray(Property P) { if (DEBUG) log(class@"Checking Property"@P.Outer$"."$P.Name$":"@P.Class,'PropIsArray'); return P.Class == class'ArrayProperty'; }

protected static final function array<string> GenerateArray(string ArrayString) { local array<string> Members; local int i; local string S;

if (ArrayString != "") { // Remove the array wrapper ( ) ArrayString = Mid(ArrayString,1,Len(ArrayString)-2);

// If string contains internal containers, then have to split differently if (Left(ArrayString,1) == "(") { do { do { if (Left(ArrayString,1) == ")") i--; else if (Left(ArrayString,1) == "(") i++; Eat(S, ArrayString, 1); } until (i == 0);

Members[Members.Length] = S; S = ""; if (ArrayString != "" && Left(ArrayString,1) == ",") ArrayString = Mid(ArrayString,1); } until (ArrayString == ""); }

else Split2(ArrayString,",",Members); }

return Members; }

protected static final function bool NotInPlayInfo(PlayInfo PI, class<Info> NewInfo) { local int i; if (PI == None) { Warn("Invalid PlayInfo Object!"); return false; }

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

return true; }


//################################################################### //################################################################### // // Utility Methods // These are used perform various tedious operations. // // Moves Num elements from Source to Dest static final function Eat(out string Dest, out string Source, int Num) { Dest = Dest $ Left(Source, Num); Source = Mid(Source, Num); }

static final function string StringIf(bool Condition, string IfTrue, string IfFalse) { if (Condition) return IfTrue; return IfFalse; }

// Based on AccessControlIni & Object.ReplaceText() static final function string ReplaceTag(string from, string tag, coerce string with) { local int i; local string t;

// InStr() is case-sensitive i = InStr(Caps(from), Caps(tag)); while (i != -1) { t = t $ Left(from,i) $ with; from = mid(from,i+len(tag)); i = InStr(Caps(from), Caps(tag)); }

t = t $ from; return t; }

// Following functions from wUtils103 by El_Muerte[TDS] // Included by permission (copied to avoid package dependancy)

// Shifts an element off a string // example (delim = ' '): 'this is a string' -> 'is a string' // if quotechar = " : '"this is" a string' -> 'a string' static final function string StrShift(out string line, string delim, optional string quotechar) {

   local int delimpos, quotepos;
   local string result;
   if ( quotechar != "" && Left(line, Len(quotechar)) == quotechar ) {
       do {
           quotepos = InstrFrom(line, quotechar, quotepos + 1);
       } until (quotepos == -1 || quotepos + Len(quotechar) == Len(line)
               || Mid(line, quotepos + len(quotechar), len(delim)) == delim);
   }
   if ( quotepos != -1 ) {
       delimpos = InstrFrom(line, delim, quotepos);
   }
   else {
       delimpos = Instr(line, delim);
   }
   if (delimpos == -1)
   {
       result = line;
       line = "";
   }
   else {
       result = Left(line,delimpos);
       line = Mid(line,delimpos+len(delim));
   }
   if ( quotechar != "" && Left(result, Len(quotechar)) == quotechar ) {
     result = Mid(result, Len(quotechar), Len(result)-(Len(quotechar)*2));
   }
   return result;

}

// Join the elements of a string array to an array static final function string Join(array< string > ar, optional string delim, optional bool bIgnoreEmpty) {

 local string result;
 local int i;
 for (i = 0; i < ar.length; i++)
 {
   if (bIgnoreEmpty && ar[i] == "") continue;
   if (result != "") result = result$delim;
   result = result$ar[i];
 }
 return result;

}

// Fixed split method // no problems when it starts with a delim // no problems with ending spaces // delim can be a string static final function int Split2(coerce string src, string delim, out array<string> parts, optional bool ignoreEmpty, optional string quotechar) {

 local string temp;
 Parts.Remove(0, Parts.Length);
 if (delim == "" || Src == "" ) return 0;
 while (src != "")
 {
   temp = StrShift(src, delim, quotechar);
   if (temp == "")
   {
     if (!ignoreEmpty)
     {
       parts.length = parts.length+1;
       parts[parts.length-1] = temp;
     }
   }
   else {
     parts.length = parts.length+1;
     parts[parts.length-1] = temp;
   }
 }
 return parts.length;

}

// InStr starting from an offset static final function int InStrFrom(coerce string StrText, coerce string StrPart, optional int OffsetStart) {

 local int OffsetPart;
 OffsetPart = InStr(Mid(StrText, OffsetStart), StrPart);
 if (OffsetPart >= 0)
   OffsetPart += OffsetStart;
 return OffsetPart;

} </uscript>


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