Legacy:AutoLoader
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"@ThisEntry.ClassFrom@ThisEntry.PropName@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