Legacy:LadderGameRules
From Unreal Wiki, The Unreal Engine Documentation Site
//-----------------------------------------------------------
// Ladder.LadderGameRules
//
// Class responsible for maintenance of all profiles
//
// Handles all interface between game & profiles
// Handles all interaction from webadmin interface & adminmenu interface
//-----------------------------------------------------------
class LadderGameRules extends GameRules
config;
const LOGNAME = 'LadderGameRules';
// Handles all ServerActors with PlayInfo
var ConfigMaster SAManager;
// Server was shut down before game was finished
var config bool bDirtyStart;
// Profiles
struct Profile
{
var string ProfileName; // Name of Profile
var bool bActive; // This profile is currently in use
var bool bReload; // This profile was the last profile to be used
};
// Profile Storage
var protected config array<Profile> Profiles;
var protected config int NumRemainingMatches;
// Profile Usage Variables
var array<ProfileConfigSet> LadderProfiles;
var ProfileConfigSet CurrentProfile; // Currently Active Profile
var StringArray AllLadderProfiles; // Only used for sorting in switch profiles by name
var StringArray ProfileMaps; // Holds maplist for switching server to new profile
var string ProfileMutators; // Holds mutator string for switch server to new profile
var array<string> ProfileActors;
var bool bWaitingToSwitch; // Waiting for end of match to switch server to new profile
var int Index; // Index of currently active profile
var string LadderProfileClass;
var string DefaultProfileName;
var class<ProfileConfigSet> ProfileClass;
struct CommandLineParam { var string ParamName; var string Value; };
var array<CommandLineParam> CommandLineOptions;
// Localization
var localized string ShutdownWhileTemporary;
var localized string ShutdownWhilePermanent;
var localized string ShutdownWhileWaiting;
var localized string ShutdownRemainingMatches;
var localized string ShutdownResumingTemporary;
var localized string ShutdownResumingPermanent;
var localized string ShutdownTrackingCorrupted;
var localized string EndOfGame;
var localized string EndofGameIntercepting;
var localized string EndofGameNoRemainingMatches;
var localized string OverridingURL;
// Error Messages
var localized string BadProfileClass;
var localized string CannotRemoveActive;
var localized string PreviousProfileRemoved;
// Warning Messages
var localized string EmptyMaplist;
var localized string InvalidMaplist;
var localized string CancellingProfile;
var localized string BadIndex;
// Turns out there is a bug with the 2166 (and probably earlier) version
// of the MatchEnded state of DeathMatch:
// Once Level.TimeSeconds > EndTime + RestartWait, MatchEnded.Timer() begins
// calling RestartGame(). This results in RestartGame (and thus GameRules.HandleRestartGame())
// getting called multiple times.
var bool bRestartCalled;
//function bool AddTextParam(string ParamName, optional string DefaultValue, optional bool bAddIfNotExists)
//{
// local int i;
// local TextParam TmpT;
// for (
//}
function bool AddBoolParam(string ParamName, optional bool DefaultValue, optional bool bAddIfNotExists);
function bool AddSelectParam(string ParamName, array<string> VarOptions, optional bool bAddIfNotExists);
event Tick(float DeltaTime)
{
if (Level != None && Level.NextURL != "")
{
bDirtyStart = False;
SaveConfig();
Disable('Tick');
}
}
event PreBeginPlay()
{
ProfileClass = class<ProfileConfigSet>(DynamicLoadObject(LadderProfileClass, class'Class'));
if (ProfileClass == None)
{
log(BadProfileClass@"'"$LadderProfileClass$"'",LOGNAME);
Destroy();
return;
}
// Initialize String Arrays
AllLadderProfiles = new(None) class'SortedStringArray';
ProfileMaps = new(None) class'StringArray';
InitializeProfileArray();
Super.PreBeginPlay();
}
event PostBeginPlay()
{
local int i, j, Matches;
i = 0 - 1;
j = 0 - 1;
if (Level.Game.BaseMutator != None && ConfigMaster(Level.Game.BaseMutator.NextMutator) != None)
SAManager = ConfigMaster(Level.Game.BaseMutator.NextMutator);
Super.PostBeginPlay();
if (bDirtyStart)
{
i = FindPreviousProfile();
j = FindActiveProfile();
Matches = NumRemainingMatches;
if (i >= 0) // Found a previous profile
{
if (j >= 0) // Found an active profile
{
if ( Matches > 0 ) // Temporary profile was active
{
log(ShutdownWhileTemporary@"'"$Profiles[j].ProfileName$"'.",LOGNAME);
log(ShutdownRemainingMatches$":"@NumRemainingMatches$"."@ShutdownResumingTemporary,LOGNAME);
ApplyProfile( j, Matches );
}
else
{
log(ShutdownWhileTemporary@"'"$Profiles[j].ProfileName$"'.",LOGNAME);
log(ShutdownRemainingMatches$":"@NumRemainingMatches$"."@ShutdownResumingPermanent@"'"$Profiles[i].ProfileName$"'.",LOGNAME);
ApplyProfile( i, 0);
}
}
else
{
log(ShutdownWhileWaiting,LOGNAME); // Shutdown while waiting to apply temporary profile - cancel
if ( Matches > 0 )
{
ResetTrackingValues();
ApplyProfile(i,Matches);
}
else
{
ResetTrackingValues();
ApplyProfile(i,0);
}
}
}
else if (j >= 0) // no reload, but found active profile
{
log(ShutdownWhilePermanent@"'"$Profiles[j].ProfileName$"'."@ShutdownResumingPermanent@"'"$Profiles[j].ProfileName$"'.",LOGNAME);
ApplyProfile( j, 0 );
}
else if (Matches > 0) // no reload, no active
{
NumRemainingMatches = 0;
SaveConfig();
}
}
else
{
bDirtyStart = True;
SaveConfig();
}
}
// Game has ended - even if we want to handle restart, cannot simply
// return True, or GameRulesModifiers later in the list do not recieve
// HandleRestartGame() calls
function bool HandleRestartGame()
{
local int i;
local bool bHandleRestart; // True if we are handling restart
if (bDirtyStart)
{
bDirtyStart = False;
SaveConfig();
}
if (!Level.Game.bChangeLevels || Level.Game.bAlreadyChanged)
return Super.HandleRestartGame();
if (bRestartCalled)
return Super.HandleRestartGame();
else bRestartCalled = True;
if ( bWaitingToSwitch )
{
i = FindPreviousProfile();
if (i >= 0)
{
log(EndOfGame,LOGNAME);
log(EndofGameIntercepting@"'"$Profiles[i].ProfileName$"'.",LOGNAME);
ApplyProfile(i,NumRemainingMatches);
bHandleRestart = True;
}
}
else if ( NumRemainingMatches > 0)
{
if (NumRemainingMatches == 1)
{
// Time to switch server to previous profile
i = FindPreviousProfile();
if (i >= 0)
{
log(EndOfGame,LOGNAME);
log(EndOfGameNoRemainingMatches,LOGNAME);
log(EndOfGameIntercepting@"'"$Profiles[i].ProfileName$"'.",LOGNAME);
ApplyProfile(i,0);
bHandleRestart = true;
}
else
{
log(EndOfGame,LOGNAME);
log(PreviousProfileRemoved,LOGNAME);
}
}
if ( !bHandleRestart )
{
NumRemainingMatches--;
SaveConfig();
}
}
// Returns true if either one is true
return (Super.HandleRestartGame() || bHandleRestart);
}
//#########################################
// Profile Management
//
// Applies selected profile immediately
function ApplyProfile(coerce int NewProfileIndex, int NumberOfMatches)
{
local int idx,i,j;
local ProfileConfigSet TempPCS;
local array<string> TempArr;
local string GameType,ShortName,FirstMap,Tmp,TmpV;
idx = ActivateProfile(NewProfileIndex, NumberOfMatches > 0);
if (idx < 0)
{
Warn(CancellingProfile@"'"$Profiles[NewProfileIndex].ProfileName$"'"@"("$BadIndex$")");
if (ActivateProfile(Index) < 0)
ResetTrackingValues();
return;
}
TempPCS = LadderProfiles[idx];
class'Ladder.CommandLineParams'.default.bRejectPlayInfo = True;
TempPCS.StartEdit();
GameType = string(TempPCS.GetGameClass());
// Set the maplist
ProfileMaps.Reset();
TempArr = TempPCS.GetUsedMaps();
if (TempArr.Length == 0)
{
Warn(CancellingProfile@"'"$Profiles[idx].ProfileName$"'"@"("$EmptyMaplist$")");
if (Index == idx) DeactivateProfile(Index);
else if (ActivateProfile(Index, False) < 0)
ResetTrackingValues();
TempPCS.EndEdit(False);
return;
}
NumRemainingMatches = NumberOfMatches;
for (i=0;i<TempArr.Length;i++)
ProfileMaps.Add(string(i),TempArr[i]);
FirstMap = ProfileMaps.getTag(0);
UpdateDefaultMaps(GameType,ProfileMaps);
//log("TempPCS:"$TempPCS@"ConfigManager:"$SAManager,'ApplyProfile');
// Set the mutators
ProfileMutators = "";
ProfileMutators = UpdateMutatorString(TempPCS);
// Set the server actors
ProfileActors = TempPCS.GetUsedServerActors();
TempArr = SAManager.GetAllManagedActors();
for (i = 0; i < TempArr.Length; i++)
{
Tmp = Left(TempArr[i],InStr(TempArr[i],","));
for (j = 0; j < ProfileActors.Length; j++)
{
if (Tmp ~= ProfileActors[j])
break;
}
if (j < ProfileActors.Length)
EnableManagedActor(Tmp);
else DisableManagedActor(Tmp);
}
// Set the PlayInfo
for (i=0;i<TempPCS.Count();i++)
{
Tmp = TempPCS.GetProfileParamName(i);
TmpV = TempPCS.GetProfileParam(i);
ShortName = GetItemName(Tmp);
// If this parameter was specified at the URL,
// make sure to replace it, otherwise the URL value will
// override the profile value
if (HasURLOption(ShortName))
{
log(OverridingURL@ShortName$".",LOGNAME);
UpdateURL(ShortName, TmpV, false);
}
else if (!TempPCS.SetNamedParam(Tmp, TmpV))
{
for (j = 0; j < CommandLineOptions.Length; j++)
{
if (Tmp ~= CommandLineOptions[j].ParamName && TmpV != "")
UpdateURL(ShortName, TmpV, false);
}
}
}
TempPCS.SavePI();
TempPCS.EndEdit(False);
if (bDirtyStart)
{
bDirtyStart = False;
SaveConfig();
}
Level.ServerTravel(FirstMap$"?Game="$GameType$"?Mutator="$ProfileMutators, False);
}
// Applies selected profile at the end of the current match
function bool WaitApplyProfile(coerce int NewProfileIndex, int NumberOfMatches)
{
local int i;
local int CurrentActive;
CurrentActive = FindActiveProfile();
// Check to make sure we aren't attempting to switch to the currently active profile
if ( NewProfileIndex == CurrentActive)
return false;
// Clean up any other profiles that were supposed to be reloaded
i = FindPreviousProfile();
if (i >= 0) Profiles[i].bReload = False;
if (CurrentActive >= 0) Profiles[CurrentActive].bActive = False;
Profiles[NewProfileIndex].bReload = True;
bWaitingToSwitch = True;
NumRemainingMatches = NumberOfMatches;
SaveConfig();
return true;
}
// Add new profile
function ProfileConfigSet AddProfile(string PCSName, optional string GameType)
{
local Profile TempP;
local int i, NumProfiles;
NumProfiles = Profiles.Length;
for (i = 0; i < NumProfiles; i++)
if (Profiles[i].ProfileName ~= PCSName)
return None;
TempP.ProfileName = PCSName;
Profiles[Profiles.Length] = TempP;
LoadProfile(TempP,GameType);
SaveConfig();
return LadderProfiles[NumProfiles];
}
// Copy profile - NewProfileName must be unique
function bool CopyProfile(int SourceProfileIndex, string NewProfileName)
{
local ProfileConfigSet PCS;
if (SourceProfileIndex < 0 || SourceProfileIndex >= Profiles.Length)
return false;
if (LadderProfiles[SourceProfileIndex] == None)
return false;
PCS = AddProfile(NewProfileName);
if (PCS != None)
{
LadderProfiles[SourceProfileIndex].StartEdit();
PCS.StartEdit();
PCS.ReplaceWith(LadderProfiles[SourceProfileIndex]);
PCS.EndEdit(True);
LadderProfiles[SourceProfileIndex].EndEdit(False);
return true;
}
return false;
}
// Remove profile
function bool RemoveProfile(int idx)
{
local ProfileConfigSet LastPCS;
local int LastIDX;
if (idx < 0 || idx >= LadderProfiles.Length)
return false;
if (Profiles[idx].bActive)
{
log(CannotRemoveActive,LOGNAME);
return false;
}
LastIDX = LadderProfiles.Length - 1;
LastPCS = LadderProfiles[LastIDX];
LastPCS.StartEdit();
if (idx < LastIDX)
{
Profiles[idx] = Profiles[LastIDX];
LadderProfiles[idx].StartEdit();
LadderProfiles[idx].ReplaceWith(LastPCS);
LadderProfiles[idx].EndEdit(True);
AllLadderProfiles.Remove(AllLadderProfiles.FindItemId(string(idx)));
AllLadderProfiles.Add(string(idx),Profiles[LastIDX].ProfileName);
}
LastPCS.Wipe();
LastPCS.EndEdit(True);
Profiles.Remove(LastIDX,1);
LadderProfiles.Remove(LastIDX,1);
AllLadderProfiles.Remove(AllLadderProfiles.FindItemId(string(LastIDX)));
SaveConfig();
return true;
}
// Returns index of currently active profile
function int FindActiveProfile()
{
local int i;
if (Index < -1)
{
for (i=0;i<Profiles.Length;i++)
if (Profiles[i].bActive)
Index = i;
if (Index < -1) Index++;
}
return Index;
}
// Returns index of previously active profile
function int FindPreviousProfile()
{
local int i;
for (i=0;i<Profiles.Length;i++)
if (Profiles[i].bReload)
return i;
return -1;
}
//#####################################################
// Profile Interface
//
// Returns an array of map names that in the maplist for this profile
function array<string> GetProfileMaps(optional string CurrentProfileItem)
{
local int j;
local ProfileConfigSet PCS;
local array<string> TempMaps;
if (CurrentProfileItem != "")
{
j = int(CurrentProfileItem);
PCS = LadderProfiles[j];
}
if (PCS == None && CurrentProfile!=None)
PCS = CurrentProfile;
if (PCS != None)
TempMaps = PCS.GetUsedMaps();
return TempMaps;
}
// Returns an array of class names for mutators that this profile will use in the game
function array<string> GetProfileMutators(optional string CurrentProfileItem)
{
local int j;
local ProfileConfigSet PCS;
local array<string> TempString;
if (CurrentProfileItem != "")
{
j = int(CurrentProfileItem);
PCS = LadderProfiles[j];
}
if (PCS == None && CurrentProfile != None)
PCS = CurrentProfile;
// Query profile for mutators to display
if (PCS != none)
TempString = PCS.GetUsedMutators();
return TempString;
}
function array<string> GetProfileServerActors(optional string CurrentProfileItem)
{
local int j;
local ProfileConfigSet PCS;
local array<string> Temp;
if (CurrentProfileItem != "")
{
j = int(CurrentProfileItem);
PCS = LadderProfiles[j];
}
if (PCS == None && CurrentProfile != None)
PCS = CurrentProfile;
if (PCS != None)
Temp = PCS.GetUsedServerActors();
return Temp;
}
//#####################################################
// Protected helper functions
//
// Clears LadderProfiles array and loads each stored (Profiles array)
// profile to the LadderProfiles array
protected function InitializeProfileArray()
{
local int i;
LadderProfiles.Length = 0;
// Load all profiles immediately
for (i = 0; i < Profiles.Length; i++)
LoadProfile(Profiles[i]);
}
// Adds a profile to the LadderProfiles array & initializes the profile
protected function LoadProfile(Profile NewProfile, optional string GameType)
{
local int i;
i = LadderProfiles.Length;
LadderProfiles.Length = LadderProfiles.Length + 1;
LadderProfiles[i] = new(None,"Profile"$string(i)) ProfileClass;
if (LadderProfiles[i].GetGameClass() != None)
{
LadderProfiles[i].StartEdit();
LadderProfiles[i].Wipe();
LadderProfiles[i].EndEdit(True);
}
if (GameType == "") LadderProfiles[i].Init(Level);
else LadderProfiles[i].Init(Level,GameType);
AllLadderProfiles.Add(string(i), NewProfile.ProfileName);
}
protected function int ActivateProfile(coerce int ProfileId, optional bool bStoreLast)
{
local int i,j;
if (ProfileId < 0 || ProfileId >= Profiles.Length)
return -1;
i = FindActiveProfile();
if (i == ProfileId) return ProfileId;
j = 0 - 1;
if (i >= 0) j = DeactivateProfile(i);
if (j >= 0)
{
if (bStoreLast) Profiles[j].bReload = True;
else Profiles[j].bReload = False;
}
Profiles[ProfileId].bActive = True;
Profiles[ProfileId].bReload = False;
SaveConfig();
return ProfileId;
}
protected function int DeactivateProfile(coerce int j)
{
if (j < 0 || j > Profiles.Length)
return -1;
Profiles[j].bActive = False;
Index = default.Index;
return j;
}
// Copied from UTServerAdmin - Applies the profile's maplist to the server's maplist
function UpdateDefaultMaps(String GameType, StringArray Maps)
{
local class<GameInfo> GameClass;
local MapList List;
local int i;
if (Maps == None)
{
Warn(InvalidMaplist);
return;
}
GameClass = class<GameInfo>(DynamicLoadObject(GameType, class'Class'));
if (GameClass != None && GameClass.Default.MapListType != "")
{
List = Level.Game.GetMapList(GameClass.Default.MapListType);
if (List != None)
{
List.Maps.Length = 0;
for (i=0; i<Maps.Count(); i++)
List.Maps[i] = Maps.GetTag(i);
List.MapNum = 0;
List.SaveConfig();
List.Destroy();
}
}
}
function EnableManagedActor(string ActorName)
{
local int i;
local string S;
local class<AutoLoader> A;
local array<string> Arr;
if (SAManager == None) return;
for (i = 0; i < SAManager.LoaderClasses.Length; i++)
{
A = SAManager.LoaderClasses[i].AutoLoaderClass;
if (A == None) continue;
Arr = A.static.GetManagedActors();
while (Arr.Length > 0)
{
S = class'wUtils103.wArray'.static.ShiftS(Arr);
if (ActorName ~= Left(S, InStr(S,",")))
{
A.static.EnableLoader(ActorName);
return;
}
}
}
}
function DisableManagedActor(string ActorName)
{
local int i;
local string S;
local class<AutoLoader> A;
local array<string> Arr;
if (SAManager == None) return;
for (i = 0; i < SAManager.LoaderClasses.Length; i++)
{
A = SAManager.LoaderClasses[i].AutoLoaderClass;
if (A == None) continue;
Arr = A.static.GetManagedActors();
while (Arr.Length > 0)
{
S = class'wUtils103.wArray'.static.ShiftS(Arr);
if (ActorName ~= Left(S, InStr(S,",")))
{
A.static.DisableLoader(ActorName);
return;
}
}
}
}
// Replaces the ?mutator= command line parameter with this profile's configured mutators
function string UpdateMutatorString(ProfileConfigSet PCS)
{
local array<string> Mutators;
local string MutatorString;
if (PCS == None)
return "";
Mutators = PCS.GetUsedMutators();
MutatorString = class'wUtils103.wArray'.static.Join(Mutators,",",True);
return MutatorString;
}
// Copied from UTServerAdmin and modified
final function bool HasURLOption(string ParamName)
{
local string Param, Value;
local int i;
Param = ParamName;
while (true)
{
i = Instr(Param, ".");
if (i < 0)
break;
Param = Mid(Param, i+1);
}
Value = Level.GetUrlOption(Param);
return Value != "";
}
protected function ResetTrackingValues()
{
local int i;
for (i=0;i<Profiles.Length;i++)
{
Profiles[i].bActive = False;
Profiles[i].bReload = False;
}
NumRemainingMatches = 0;
SaveConfig();
}
function int GetRemainingMatches()
{
return NumRemainingMatches;
}
defaultproperties
{
Index=-2
LadderProfileClass="Ladder.ProfileConfigSet"
ShutdownWhileTemporary="Server was shutdown with active temporary profile"
ShutdownWhilePermanent="Server was shutdown with active profile"
ShutdownWhileWaiting="Server was shutdown while waiting to apply a profile. Now applying new profile."
ShutdownRemainingMatches="Number of matches remaining for this profile"
ShutdownResumingTemporary="Reactivating temporary profile with remaining number of matches."
ShutdownResumingPermanent="Reactivating standard profile"
ShutdownTrackingCorrupted="Profile tracking was corrupted. Resetting profile tracking values."
EndOfGame="END OF GAME DETECTED."
EndofGameIntercepting="Intercepting map change & activating profile"
EndofGameNoRemainingMatches="Profile match limit reached. Reactivating previous profile."
OverridingURL="Overriding command line value for parameter"
BadProfileClass="Bad Profile Class"
CannotRemoveActive="Removal of active profile not allowed! Please switch server to a different profile, then try again."
PreviousProfileRemoved="Attempted to return server to previous profile, but no previous profile was found. Leaving server set to current profile."
BadIndex="Invalid profile index"
CancellingProfile="Cancelling activation of profile"
EmptyMaplist="Profile maplist is empty"
InvalidMaplist="Attempting to load profile with invalid maplist!"
CommandLineOptions(0)=(ParamName="CommandLineParams.AdminUserName",Value="")
CommandLineOptions(1)=(ParamName="CommandLineParams.AdminPassword",Value="")
CommandLineOptions(2)=(ParamName="CommandLineParams.GameRules",Value="")
CommandLineOptions(3)=(ParamName="CommandLineParams.DemoRec",Value="")
CommandLineOptions(4)=(ParamName="CommandLineParams.bAutoNumBots",Value="")
CommandLineOptions(5)=(ParamName="CommandLineParams.QuickStart",Value="")
CommandLineOptions(6)=(ParamName="CommandLineParams.RedTeamAI",Value="UnrealGame.TeamAI")
CommandLineOptions(7)=(ParamName="CommandLineParams.BlueTeamAI",Value="UnrealGame.TeamAI")
CommandLineOptions(8)=(ParamName="CommandLineParams.RedTeamSymbol",Value="")
CommandLineOptions(9)=(ParamName="CommandLineParams.BlueTeamSymbol",Value="")
}