888 lines
29 KiB
Ucode
888 lines
29 KiB
Ucode
|
//=============================================================================
|
||
|
// AutoTestManager
|
||
|
//
|
||
|
// The AutoTestManager is spawned by the GameInfo if requested by the URL.
|
||
|
// It provides an interface for performing in gameplay automated testing.
|
||
|
//
|
||
|
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
||
|
//=============================================================================
|
||
|
class AutoTestManager extends Info
|
||
|
native
|
||
|
config(Game);
|
||
|
|
||
|
//=================================================================
|
||
|
// AUTOMATED PERFORMANCE TESTING PROPERTIES
|
||
|
/** Whether the game is currently in automated perf test mode. */
|
||
|
var bool bAutomatedPerfTesting;
|
||
|
|
||
|
/** Amount of time remaining before match ends -- used for auto performance test shutdown */
|
||
|
var int AutomatedPerfRemainingTime;
|
||
|
|
||
|
/** This will auto continue to the next round. Very useful doing soak testing and testing traveling to next level **/
|
||
|
var bool bAutoContinueToNextRound;
|
||
|
|
||
|
/** Whether or not we are using the Automated Testing Map list **/
|
||
|
var bool bUsingAutomatedTestingMapList;
|
||
|
|
||
|
/** If TRUE, use OpenMap to transition; if FALSE, use SeamlessTravel **/
|
||
|
var bool bAutomatedTestingWithOpen;
|
||
|
|
||
|
/**
|
||
|
* The index of the current automated testing map.
|
||
|
* If < 0 we are in the transition map.
|
||
|
**/
|
||
|
var int AutomatedTestingMapIndex;
|
||
|
|
||
|
/** List of maps that we are going to be using for the AutomatedMapTesting **/
|
||
|
var globalconfig array<string> AutomatedMapTestingList;
|
||
|
|
||
|
/** Number of times to run through the list. (0 in infinite) **/
|
||
|
var globalconfig int NumAutomatedMapTestingCycles;
|
||
|
|
||
|
/** Whether to exit when NumAutomatedMapTestingCycles is reached */
|
||
|
var globalconfig bool bExitOnCyclesComplete;
|
||
|
|
||
|
/**
|
||
|
* Number of matches played (maybe remove this before shipping)
|
||
|
* This is really useful for doing soak testing and such to see how long you lasted!
|
||
|
* NOTE: This is not replicated out to clients atm.
|
||
|
**/
|
||
|
var int NumberOfMatchesPlayed;
|
||
|
|
||
|
/** Keeps track of the current run so when we have repeats and such we know how far along we are **/
|
||
|
var int NumMapListCyclesDone;
|
||
|
|
||
|
/** This will be run at the start of each start match **/
|
||
|
var string AutomatedTestingExecCommandToRunAtStartMatch;
|
||
|
|
||
|
/** This will be the 'transition' map used w/ OpenMap runs **/
|
||
|
var string AutomatedMapTestingTransitionMap;
|
||
|
|
||
|
/**
|
||
|
* Whether or not this game should check for fragmentation. This can be used to have a specific game type check for fragmentation at some point
|
||
|
* (e.g. start/end of match, time period)
|
||
|
**/
|
||
|
var bool bCheckingForFragmentation;
|
||
|
|
||
|
/** Whether or not this game should check for memory leaks **/
|
||
|
var bool bCheckingForMemLeaks;
|
||
|
|
||
|
//====================================================================
|
||
|
// SENTINEL PROPERTIES
|
||
|
/** Whether or this game is doing a bDoingASentinelRun test **/
|
||
|
var bool bDoingASentinelRun;
|
||
|
|
||
|
/** Used for the BeginRun Task___ strings, examples "FlyThrough", "FlyThroughSplitScreen", "BVT" */
|
||
|
var String SentinelTaskDescription;
|
||
|
|
||
|
/** Used for the BeginRun Task___ strings */
|
||
|
var String SentinelTaskParameter;
|
||
|
|
||
|
/** Used for the BeginRun Task___ strings */
|
||
|
var String SentinelTagDesc;
|
||
|
|
||
|
/** PlayerController used for Sentinel - picked randomly */
|
||
|
var transient PlayerController SentinelPC;
|
||
|
|
||
|
/** Locations where sentinel should go to */
|
||
|
var transient array<vector> SentinelTravelArray;
|
||
|
|
||
|
/** Iterator for looping through SentinelTravelArray - for loop is in state code, so can't define locally */
|
||
|
var transient int SentinelNavigationIdx;
|
||
|
|
||
|
/** Iterator for various sentinel state code loops - for loop is in state code, so can't define locally */
|
||
|
var transient int SentinelIdx;
|
||
|
|
||
|
/** Used to delay until streaming levels are fully loaded */
|
||
|
var transient bool bSentinelStreamingLevelStillLoading;
|
||
|
|
||
|
/** Change increments for iterating through rotations used at sentinel travel locations */
|
||
|
var transient int NumRotationsIncrement;
|
||
|
|
||
|
/** Change increments for iterating through sentinel travel locations */
|
||
|
var transient int TravelPointsIncrement;
|
||
|
|
||
|
/** How many minutes per map we are allowed to run. **/
|
||
|
var config int NumMinutesPerMap;
|
||
|
|
||
|
/**
|
||
|
* At each TravelTheWorld node we fire off all of the commands in this array. This is good for being able to
|
||
|
* do things like fire off a debug command without having to recook the entire map (e.g. MemLeakCheck at each node).
|
||
|
**/
|
||
|
var config array<String> CommandsToRunAtEachTravelTheWorldNode;
|
||
|
|
||
|
/** Transient string that we need for our foreach in the TravelTheWorld state code **/
|
||
|
var transient String CommandStringToExec;
|
||
|
|
||
|
event PostBeginPlay()
|
||
|
{
|
||
|
Super.PostBeginPlay();
|
||
|
|
||
|
SetTimer(1.0, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Base AutoTestManager timer ticks once per second
|
||
|
* Checks if perf test timer has run out
|
||
|
*/
|
||
|
event Timer()
|
||
|
{
|
||
|
if( bAutomatedPerfTesting && (AutomatedPerfRemainingTime > 0) && !bAutoContinueToNextRound )
|
||
|
{
|
||
|
AutomatedPerfRemainingTime--;
|
||
|
if( AutomatedPerfRemainingTime <= 0 )
|
||
|
{
|
||
|
// Exit at the end of the match if automated perf testing is enabled.
|
||
|
ConsoleCommand("EXIT");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize AutoTestManager based on command line options
|
||
|
* @param Options is the full command line options string passed to GameInfo
|
||
|
*/
|
||
|
function InitializeOptions(string Options)
|
||
|
{
|
||
|
local string InOpt;
|
||
|
|
||
|
AutomatedPerfRemainingTime = 60 * WorldInfo.Game.TimeLimit;
|
||
|
|
||
|
bAutomatedPerfTesting = ( WorldInfo.Game.ParseOption( Options, "AutomatedPerfTesting" ) ~= "1" ) || ( WorldInfo.Game.ParseOption( Options, "gAPT" ) ~= "1" );
|
||
|
|
||
|
bCheckingForFragmentation = ( WorldInfo.Game.ParseOption( Options, "CheckingForFragmentation" ) ~= "1" ) || ( WorldInfo.Game.ParseOption( Options, "gCFF" ) ~= "1" );
|
||
|
|
||
|
bCheckingForMemLeaks = ( WorldInfo.Game.ParseOption( Options, "CheckingForMemLeaks" ) ~= "1" ) || ( WorldInfo.Game.ParseOption( Options, "gCFML" ) ~= "1" );
|
||
|
bDoingASentinelRun = ( WorldInfo.Game.ParseOption( Options, "DoingASentinelRun" ) ~= "1" ) || ( WorldInfo.Game.ParseOption( Options, "gDASR" ) ~= "1" );
|
||
|
|
||
|
SentinelTaskDescription = ( WorldInfo.Game.ParseOption( Options, "SentinelTaskDescription" ) );
|
||
|
if( SentinelTaskDescription == "" )
|
||
|
{
|
||
|
SentinelTaskDescription = ( WorldInfo.Game.ParseOption( Options, "gSTD" ) );
|
||
|
}
|
||
|
|
||
|
SentinelTaskParameter = ( WorldInfo.Game.ParseOption( Options, "SentinelTaskParameter" ) );
|
||
|
if( SentinelTaskParameter == "" )
|
||
|
{
|
||
|
SentinelTaskParameter = ( WorldInfo.Game.ParseOption( Options, "gSTP" ) );
|
||
|
}
|
||
|
|
||
|
SentinelTagDesc = ( WorldInfo.Game.ParseOption( Options, "SentinelTagDesc" ) );
|
||
|
if( SentinelTagDesc == "" )
|
||
|
{
|
||
|
SentinelTagDesc = ( WorldInfo.Game.ParseOption( Options, "gSTDD" ) );
|
||
|
}
|
||
|
|
||
|
InOpt = WorldInfo.Game.ParseOption( Options, "AutoContinueToNextRound");
|
||
|
if( InOpt != "" )
|
||
|
{
|
||
|
`log("AutoContinueToNextRound: "$bool(InOpt));
|
||
|
bAutoContinueToNextRound = bool(InOpt);
|
||
|
}
|
||
|
|
||
|
|
||
|
InOpt = WorldInfo.Game.ParseOption( Options, "bUsingAutomatedTestingMapList");
|
||
|
if( InOpt != "" )
|
||
|
{
|
||
|
`log("bUsingAutomatedTestingMapList: "$bool(InOpt));
|
||
|
bUsingAutomatedTestingMapList = bool(InOpt);
|
||
|
}
|
||
|
|
||
|
if ( bUsingAutomatedTestingMapList )
|
||
|
{
|
||
|
if (AutomatedMapTestingList.length == 0)
|
||
|
{
|
||
|
`log("*** No maps in automated test map list... Disabling bUsingAutomatedTestingMapList");
|
||
|
bUsingAutomatedTestingMapList = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
InOpt = WorldInfo.Game.ParseOption( Options, "bAutomatedTestingWithOpen");
|
||
|
if( InOpt != "" )
|
||
|
{
|
||
|
`log("bAutomatedTestingWithOpen: "$bool(InOpt));
|
||
|
bAutomatedTestingWithOpen = bool(InOpt);
|
||
|
}
|
||
|
|
||
|
AutomatedTestingExecCommandToRunAtStartMatch = WorldInfo.Game.ParseOption( Options, "AutomatedTestingExecCommandToRunAtStartMatch");
|
||
|
`log("AutomatedTestingExecCommandToRunAtStartMatch: "$AutomatedTestingExecCommandToRunAtStartMatch);
|
||
|
|
||
|
AutomatedMapTestingTransitionMap = WorldInfo.Game.ParseOption( Options, "AutomatedMapTestingTransitionMap");
|
||
|
`log("AutomatedMapTestingTransitionMap: "$AutomatedMapTestingTransitionMap);
|
||
|
|
||
|
InOpt = WorldInfo.Game.ParseOption(Options, "AutomatedTestingMapIndex");
|
||
|
if (InOpt != "")
|
||
|
{
|
||
|
`log("AutomatedTestingMapIndex: " $ int(InOpt));
|
||
|
AutomatedTestingMapIndex = int(InOpt);
|
||
|
}
|
||
|
|
||
|
if ( bAutomatedTestingWithOpen )
|
||
|
{
|
||
|
InOpt = WorldInfo.Game.ParseOption(Options, "NumberOfMatchesPlayed");
|
||
|
if (InOpt != "")
|
||
|
{
|
||
|
`log("NumberOfMatchesPlayed: " $ int(InOpt));
|
||
|
NumberOfMatchesPlayed = int(InOpt);
|
||
|
}
|
||
|
|
||
|
InOpt = WorldInfo.Game.ParseOption(Options, "NumMapListCyclesDone");
|
||
|
if (InOpt != "")
|
||
|
{
|
||
|
`log("NumMapListCyclesDone: " $ int(InOpt));
|
||
|
NumMapListCyclesDone = int(InOpt);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Server travel uses a transition map automatically...
|
||
|
`log("*** Disabling automated transition map for ServerTravel");
|
||
|
AutomatedMapTestingTransitionMap = "";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//// SENTINEL FUNCTIONS START
|
||
|
/**
|
||
|
* This will start a SentinelRun in the DB. Setting up the Run table with all of metadata that a run has.
|
||
|
* This will also set the GSentinelRunID so that the engine knows what the current run is.
|
||
|
*
|
||
|
* @param TaskDescription The name/description of the task that we are running
|
||
|
* @param TaskParameter Any Parameters that the task needs
|
||
|
* @param TagDesc A specialized tag (e.g. We are doing Task A with Param B and then "MapWithParticlesAdded" so it can be found)
|
||
|
**/
|
||
|
native function BeginSentinelRun( const string TaskDescription, const string TaskParameter, const string TagDesc );
|
||
|
|
||
|
|
||
|
/**
|
||
|
* This will output some set of data that we care about when we are doing Sentinel runs while we are
|
||
|
* doing a MP test or a BVT.
|
||
|
* Prob just stat unit and some other random stats (streaming fudge factor and such)
|
||
|
**/
|
||
|
native function AddSentinelPerTimePeriodStats( const vector InLocation, const rotator InRotation );
|
||
|
|
||
|
|
||
|
/**
|
||
|
* This will tell the DB to end the current Sentinel run (i.e. GSentinelRunID) and set that Run's RunResult to the passed in var.
|
||
|
*
|
||
|
* @param RunResult The result of this Sentinel run (e.g. OOM, Passed, etc.)
|
||
|
**/
|
||
|
native function EndSentinelRun( EAutomatedRunResult RunResult );
|
||
|
|
||
|
//// This code is our "travel the map and get Sentinel data at each point ////
|
||
|
// so we need to do:
|
||
|
// 0) turn off streaming volume streaming of levels bIsUsingStreamingVolumes
|
||
|
// 1) unload all levels StreamLevelOut( )
|
||
|
// 2) for each level in the P level
|
||
|
// load it and then grab all of the locations of the NavigationPoints and store it
|
||
|
// 3) turn back on the streaming volume support
|
||
|
// 4) iterate down the list of locations
|
||
|
|
||
|
/**
|
||
|
* function to start the world traveling
|
||
|
**/
|
||
|
function DoTravelTheWorld()
|
||
|
{
|
||
|
GotoState( 'TravelTheWorld' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This our state which allows us to have delayed actions while traveling the world (e.g. waiting for levels to stream in)
|
||
|
**/
|
||
|
state TravelTheWorld
|
||
|
{
|
||
|
function BeginState( name PreviousStateName )
|
||
|
{
|
||
|
local PlayerController PC;
|
||
|
`log( "BeginState TravelTheWorld" );
|
||
|
|
||
|
Super.BeginState( PreviousStateName );
|
||
|
|
||
|
foreach LocalPlayerControllers( class'PlayerController', PC )
|
||
|
{
|
||
|
SentinelPC = PC;
|
||
|
SentinelPC.Sentinel_SetupForGamebasedTravelTheWorld();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
SentinelPC.bIsUsingStreamingVolumes = FALSE;
|
||
|
|
||
|
BeginSentinelRun( SentinelTaskDescription, SentinelTaskParameter, SentinelTagDesc );
|
||
|
}
|
||
|
|
||
|
function float CalcTravelTheWorldTime( const int NumTravelLocations, const int NumRotations )
|
||
|
{
|
||
|
local float TotalTimeInSeconds;
|
||
|
local float PerTravelLocTime;
|
||
|
|
||
|
// streaming out all maps
|
||
|
TotalTimeInSeconds += WorldInfo.StreamingLevels.length * 2.0f;
|
||
|
TotalTimeInSeconds += 10.0f;
|
||
|
|
||
|
// gathering travel locations
|
||
|
TotalTimeInSeconds += WorldInfo.StreamingLevels.length * 10.0f;
|
||
|
TotalTimeInSeconds += 10.0f;
|
||
|
|
||
|
// wait for kill all
|
||
|
TotalTimeInSeconds += 10.0f;
|
||
|
|
||
|
// 4.0f is the avg cost for waiting for levels to stream in (guess basically)
|
||
|
// we do two rotations as we want to capture UnitFPS data without any text on the screen
|
||
|
PerTravelLocTime = 0.5f + 4.0f + 1.0f + 0.5f + 1.0f + (NumRotations*1.5f) + (NumRotations*1.5f);;
|
||
|
|
||
|
TotalTimeInSeconds += (PerTravelLocTime * NumTravelLocations);
|
||
|
|
||
|
|
||
|
return TotalTimeInSeconds;
|
||
|
}
|
||
|
|
||
|
function PrintOutTravelWorldTimes( const int TotalTimeInSeconds )
|
||
|
{
|
||
|
`if(`notdefined(FINAL_RELEASE))
|
||
|
local int Hours;
|
||
|
local int Minutes;
|
||
|
local int Seconds;
|
||
|
|
||
|
Hours = TotalTimeInSeconds / (60 * 60);
|
||
|
Minutes = (TotalTimeInSeconds -(Hours * 60 * 60)) / 60;
|
||
|
Seconds = TotalTimeInSeconds - (Minutes * 60) - (Hours * 60 * 60);
|
||
|
|
||
|
`log( WorldInfo.GetMapName() $ ": Traveling this map will take approx TotalSeconds: " $ TotalTimeInSeconds $ " Hours: " $ Hours $ " Minutes: " $ Minutes $ " Seconds: " $ Seconds );
|
||
|
`endif
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Modify our Increments so that we get the most number of nodes traveled to
|
||
|
* best is to travel to all doing 8 directions
|
||
|
* next is to travel to all and do 4 directions
|
||
|
* next is to travel to as many as possible across the map doing 4 directions
|
||
|
* @param NumTravelLocations is the total number of destination positions
|
||
|
*/
|
||
|
function SetIncrementsForLoops( const float NumTravelLocations )
|
||
|
{
|
||
|
local float TimeWeGetInSeconds;
|
||
|
|
||
|
// @todo be able to pass in how much time we get in seconds
|
||
|
TimeWeGetInSeconds = NumMinutesPerMap * 60;
|
||
|
|
||
|
// if we will be able to travel to all points! so only increment by 1
|
||
|
if( CalcTravelTheWorldTime( NumTravelLocations, 8 ) < TimeWeGetInSeconds )
|
||
|
{
|
||
|
TravelPointsIncrement = 1;
|
||
|
NumRotationsIncrement = 1;
|
||
|
`log( WorldInfo.GetMapName() $ " SetIncrementsForLoops: TravelPointsIncrement: " $ TravelPointsIncrement $ " NumRotationsIncrement: " $ NumRotationsIncrement $ " for NumTravelLocations: " $ NumTravelLocations);
|
||
|
PrintOutTravelWorldTimes( CalcTravelTheWorldTime( NumTravelLocations, 8 ) );
|
||
|
}
|
||
|
// we can't get to all points so let's start reducing what we do
|
||
|
else
|
||
|
{
|
||
|
// if we can get to all points but only 4 rotations
|
||
|
if( CalcTravelTheWorldTime( NumTravelLocations, 4 ) < TimeWeGetInSeconds )
|
||
|
{
|
||
|
TravelPointsIncrement = 1;
|
||
|
NumRotationsIncrement = 2; // (8/4)
|
||
|
`log( WorldInfo.GetMapName() $ " SetIncrementsForLoops: TravelPointsIncrement: " $ TravelPointsIncrement $ " NumRotationsIncrement: " $ NumRotationsIncrement $ " for NumTravelLocations: " $ NumTravelLocations);
|
||
|
PrintOutTravelWorldTimes( CalcTravelTheWorldTime( NumTravelLocations, 4 ) );
|
||
|
|
||
|
}
|
||
|
// we can't get to all points with 4 rotations so we need to increment our travelpoints faster
|
||
|
else
|
||
|
{
|
||
|
// not 100% precise but the travel time is bounded by num points
|
||
|
TravelPointsIncrement = CalcTravelTheWorldTime( NumTravelLocations, 4 ) / TimeWeGetInSeconds;
|
||
|
|
||
|
NumRotationsIncrement = 2; // (8/4)
|
||
|
`log( WorldInfo.GetMapName() $ " SetIncrementsForLoops: TravelPointsIncrement: " $ TravelPointsIncrement $ " NumRotationsIncrement: " $ NumRotationsIncrement $ " for NumTravelLocations: " $ NumTravelLocations);
|
||
|
PrintOutTravelWorldTimes( CalcTravelTheWorldTime( NumTravelLocations/TravelPointsIncrement, 4 ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
Begin:
|
||
|
// james pointed out that we could just save this out in the WorldInfo But that will take extra memory which could be thrown
|
||
|
// away for final release
|
||
|
|
||
|
SentinelPC.Sentinel_PreAcquireTravelTheWorldPoints();
|
||
|
|
||
|
// we need to do some madness here as the async nature of the streaming makes it hard to just call and have the state be correct
|
||
|
for( SentinelIdx = 0; SentinelIdx < WorldInfo.StreamingLevels.length; ++SentinelIdx )
|
||
|
{
|
||
|
`log( "StreamLevelOut: " $ Worldinfo.StreamingLevels[SentinelIdx].PackageName );
|
||
|
SentinelPC.ClientUpdateLevelStreamingStatus( Worldinfo.StreamingLevels[SentinelIdx].PackageName, FALSE, FALSE, TRUE );
|
||
|
}
|
||
|
sleep( 10.0f );
|
||
|
WorldInfo.ForceGarbageCollection( TRUE );
|
||
|
|
||
|
for( SentinelIdx = 0; SentinelIdx < WorldInfo.StreamingLevels.length; ++SentinelIdx )
|
||
|
{
|
||
|
`log( "Gathering locations for: " $ Worldinfo.StreamingLevels[SentinelIdx].PackageName );
|
||
|
SentinelPC.ClientUpdateLevelStreamingStatus( Worldinfo.StreamingLevels[SentinelIdx].PackageName, TRUE, TRUE, TRUE );
|
||
|
sleep( 7.0f ); // wait for the level to be streamed back in
|
||
|
|
||
|
GetTravelLocations( WorldInfo.StreamingLevels[SentinelIdx].PackageName, SentinelPC, SentinelTravelArray );
|
||
|
|
||
|
DoSentinelActionPerLoadedMap();
|
||
|
// turn on Memory checking
|
||
|
SentinelPC.ConsoleCommand( "FractureAllMeshesToMaximizeMemoryUsage" );
|
||
|
SentinelPC.ConsoleCommand( "stat memory" );
|
||
|
Sleep( 0.5f );
|
||
|
DoSentinel_MemoryAtSpecificLocation( vect(0,0,0), rot(0,0,0) );
|
||
|
SentinelPC.ConsoleCommand( "stat memory" );
|
||
|
|
||
|
SentinelPC.ClientUpdateLevelStreamingStatus( Worldinfo.StreamingLevels[SentinelIdx].PackageName, FALSE, FALSE, TRUE );
|
||
|
sleep( 3.0f );
|
||
|
WorldInfo.ForceGarbageCollection( TRUE );
|
||
|
}
|
||
|
|
||
|
|
||
|
// this is the Single Level case (e.g. MP_ levels)
|
||
|
if( WorldInfo.StreamingLevels.length == 0 )
|
||
|
{
|
||
|
GetTravelLocations( WorldInfo.StreamingLevels[SentinelIdx].PackageName, SentinelPC, SentinelTravelArray );
|
||
|
DoSentinelActionPerLoadedMap();
|
||
|
// turn on Memory checking
|
||
|
SentinelPC.ConsoleCommand( "FractureAllMeshesToMaximizeMemoryUsage" );
|
||
|
SentinelPC.ConsoleCommand( "stat memory" );
|
||
|
Sleep( 0.5f );
|
||
|
DoSentinel_MemoryAtSpecificLocation( vect(0,0,0), rot(0,0,0) );
|
||
|
SentinelPC.ConsoleCommand( "stat memory" );
|
||
|
|
||
|
sleep( 3.0f );
|
||
|
WorldInfo.ForceGarbageCollection( TRUE );
|
||
|
}
|
||
|
|
||
|
`log( WorldInfo.GetMapName() $ " COMPLETED LEVEL INTEROGATION!! Total TravelPoints: " $ SentinelTravelArray.Length );
|
||
|
SetIncrementsForLoops( SentinelTravelArray.Length );
|
||
|
|
||
|
|
||
|
//// so now turn back on streaming AND turn back on streaming for _LOD levels
|
||
|
|
||
|
for( SentinelIdx = 0; SentinelIdx < WorldInfo.StreamingLevels.length; ++SentinelIdx )
|
||
|
{
|
||
|
if( LevelStreamingAlwaysLoaded(Worldinfo.StreamingLevels[SentinelIdx]) != none )
|
||
|
{
|
||
|
`log( " Found a LevelStreamingAlwaysLoaded" @ Worldinfo.StreamingLevels[SentinelIdx].PackageName );
|
||
|
SentinelPC.ClientUpdateLevelStreamingStatus( Worldinfo.StreamingLevels[SentinelIdx].PackageName, TRUE, TRUE, TRUE );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SentinelPC.bIsUsingStreamingVolumes = TRUE;
|
||
|
|
||
|
|
||
|
sleep( 10.0f );
|
||
|
|
||
|
SentinelPC.Sentinel_PostAcquireTravelTheWorldPoints();
|
||
|
|
||
|
sleep( 10.0f );
|
||
|
|
||
|
// add the first point in the list to the end (so we return back there for our final MemLeakCheck)
|
||
|
SentinelTravelArray.AddItem( SentinelTravelArray[0] );
|
||
|
|
||
|
`log( "Starting Traversal" );
|
||
|
`log( " SentinelTravelArray.length " $ SentinelTravelArray.length );
|
||
|
for( SentinelNavigationIdx = 0; SentinelNavigationIdx < SentinelTravelArray.length; SentinelNavigationIdx+=TravelPointsIncrement )
|
||
|
{
|
||
|
`log( "Going to:" @ SentinelTravelArray[SentinelNavigationIdx] @ SentinelNavigationIdx $ " of " $ SentinelTravelArray.length );
|
||
|
|
||
|
SentinelPC.SetLocation( SentinelTravelArray[SentinelNavigationIdx] );
|
||
|
SentinelPC.SetRotation( rot(0,0,0) );
|
||
|
|
||
|
sleep( 0.5f );
|
||
|
|
||
|
// wait until all levels are streamed back in
|
||
|
do
|
||
|
{
|
||
|
bSentinelStreamingLevelStillLoading = FALSE;
|
||
|
|
||
|
for( SentinelIdx = 0; SentinelIdx < WorldInfo.StreamingLevels.length; ++SentinelIdx )
|
||
|
{
|
||
|
if( WorldInfo.StreamingLevels[SentinelIdx].bHasLoadRequestPending == TRUE )
|
||
|
{
|
||
|
`log( "levels not streamed in yet sleeping 1s" );
|
||
|
bSentinelStreamingLevelStillLoading = TRUE;
|
||
|
Sleep( 1.0f );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} until( bSentinelStreamingLevelStillLoading == FALSE );
|
||
|
|
||
|
WorldInfo.ForceGarbageCollection( TRUE );
|
||
|
sleep( 1.0f );
|
||
|
|
||
|
// this is our first point so grab the MemoryData
|
||
|
if( SentinelNavigationIdx == 0 )
|
||
|
{
|
||
|
ConsoleCommand( "MemLeakCheck" );
|
||
|
}
|
||
|
|
||
|
|
||
|
// turn on Memory checking
|
||
|
SentinelPC.ConsoleCommand( "stat memory" );
|
||
|
Sleep( 0.5f );
|
||
|
DoSentinel_MemoryAtSpecificLocation( SentinelPC.Location, SentinelPC.Rotation );
|
||
|
SentinelPC.ConsoleCommand( "stat memory" );
|
||
|
|
||
|
// turn on stat unit and stat Scene rendering
|
||
|
SentinelPC.ConsoleCommand( "stat scenerendering" );
|
||
|
SentinelPC.ConsoleCommand( "stat streaming" );
|
||
|
Sleep( 1.0f );
|
||
|
|
||
|
for( SentinelIdx = 0; SentinelIdx < 8; SentinelIdx+=NumRotationsIncrement )
|
||
|
{
|
||
|
//`log( "Setting rotation to: " $ 8192*SentinelIdx );
|
||
|
SentinelPC.SetRotation( rot(0,1,0)*(8192*SentinelIdx) );
|
||
|
Sleep( 1.5f );
|
||
|
DoSentinel_ViewDependentMemoryAtSpecificLocation( SentinelPC.Location, SentinelPC.Rotation );
|
||
|
}
|
||
|
|
||
|
// turn off stat unit and stat scenerendering
|
||
|
SentinelPC.ConsoleCommand( "stat scenerendering" );
|
||
|
SentinelPC.ConsoleCommand( "stat streaming" );
|
||
|
|
||
|
|
||
|
// get UnitFPS data at each rotation
|
||
|
for( SentinelIdx = 0; SentinelIdx < 8; SentinelIdx+=NumRotationsIncrement )
|
||
|
{
|
||
|
//`log( "Setting rotation to: " $ 8192*SentinelIdx );
|
||
|
SentinelPC.SetRotation( rot(0,1,0)*(8192*SentinelIdx) );
|
||
|
Sleep( 1.5f );
|
||
|
DoSentinel_PerfAtSpecificLocation( SentinelPC.Location, SentinelPC.Rotation );
|
||
|
}
|
||
|
|
||
|
//ConsoleCommand( "MemLeakCheck" );
|
||
|
foreach CommandsToRunAtEachTravelTheWorldNode( CommandStringToExec )
|
||
|
{
|
||
|
//`log( `showvar(CommandStringToExec) );
|
||
|
ConsoleCommand( CommandStringToExec );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ConsoleCommand( "MemLeakCheck" );
|
||
|
|
||
|
`log( "COMPLETED!!!!!!!" );
|
||
|
ConsoleCommand( "exit" );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* State code for handling GameInfo CauseEventCommand
|
||
|
*/
|
||
|
state SentinelHandleCauseEventCommand
|
||
|
{
|
||
|
Begin:
|
||
|
// wait until all levels are streamed back in
|
||
|
do
|
||
|
{
|
||
|
bSentinelStreamingLevelStillLoading = FALSE;
|
||
|
|
||
|
for( SentinelIdx = 0; SentinelIdx < WorldInfo.StreamingLevels.length; ++SentinelIdx )
|
||
|
{
|
||
|
if( WorldInfo.StreamingLevels[SentinelIdx].bHasLoadRequestPending == TRUE )
|
||
|
{
|
||
|
`log( "levels not streamed in yet sleeping 1s" );
|
||
|
bSentinelStreamingLevelStillLoading = TRUE;
|
||
|
Sleep( 1.0f );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} until( bSentinelStreamingLevelStillLoading == FALSE );
|
||
|
|
||
|
// check to see if we should fire off the FlyThrough event again as preround starting usually stops the first event
|
||
|
if( WorldInfo.Game.CauseEventCommand != "" )
|
||
|
{
|
||
|
foreach WorldInfo.AllControllers(class'PlayerController', SentinelPC)
|
||
|
{
|
||
|
SentinelPC.ConsoleCommand( "ce " $ WorldInfo.Game.CauseEventCommand );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// wait 500 ms to let the switching camera Hitch work itself out
|
||
|
if( ( SentinelTaskDescription == "FlyThrough" ) || ( SentinelTaskDescription == "FlyThroughSplitScreen" ) )
|
||
|
{
|
||
|
SetTimer( 0.500f, TRUE, nameof(DoTimeBasedSentinelStatGathering) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** This will run on every map load. (e.g. You have P map which consists of N sublevels. For each SubLevel this will run. **/
|
||
|
native function DoSentinelActionPerLoadedMap();
|
||
|
|
||
|
/** Add the audio related stats to the database **/
|
||
|
native function HandlePerLoadedMapAudioStats();
|
||
|
|
||
|
/** This will look at the levels and then gather all of the travel points we are interested in **/
|
||
|
native function GetTravelLocations( name LevelName, PlayerController PC, out array<vector> TravelPoints );
|
||
|
|
||
|
/** This will write out the Sentinel data at this location / rotation **/
|
||
|
native function DoSentinel_MemoryAtSpecificLocation( const vector InLocation, const rotator InRotation );
|
||
|
|
||
|
native function DoSentinel_PerfAtSpecificLocation( const out vector InLocation, const out rotator InRotation );
|
||
|
|
||
|
native function DoSentinel_ViewDependentMemoryAtSpecificLocation( const out vector InLocation, const out rotator InRotation );
|
||
|
|
||
|
|
||
|
|
||
|
/** This function should be triggered via SetTimer ever few seconds to do the Per Time Period stats gathering **/
|
||
|
function DoTimeBasedSentinelStatGathering()
|
||
|
{
|
||
|
local PlayerController PC;
|
||
|
local vector ViewLocation;
|
||
|
local rotator ViewRotation;
|
||
|
|
||
|
foreach LocalPlayerControllers( class'PlayerController', PC )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
PC.GetPlayerViewPoint( ViewLocation, ViewRotation );
|
||
|
|
||
|
// flythroughs uses the PC and not the pawn
|
||
|
if( ( SentinelTaskDescription != "FlyThrough" ) && ( SentinelTaskDescription != "FlyThroughSplitScreen" ) )
|
||
|
{
|
||
|
if( PC.Pawn != None )
|
||
|
{
|
||
|
ViewLocation = PC.Pawn.Location;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//`log( "DoTimeBasedSentinelStatGathering: " $ ViewLocation @ ViewRotation );
|
||
|
AddSentinelPerTimePeriodStats( ViewLocation, ViewRotation );
|
||
|
}
|
||
|
|
||
|
|
||
|
//// SENTINEL FUNCTIONS END
|
||
|
|
||
|
/** Determine if memory tracking will be triggered */
|
||
|
native function DoMemoryTracking();
|
||
|
|
||
|
// AUTOMATED MAP TESTING FUNCTIONS START
|
||
|
/**
|
||
|
* Start the AutomatedMapTest transition timer which will sit there and poll the status of the streaming levels.
|
||
|
* When we are doing malloc profiling and such loading is a lot slower so we can't just assume some time limit before moving on.
|
||
|
**/
|
||
|
event StartAutomatedMapTestTimer()
|
||
|
{
|
||
|
SetTimer( 5.0, TRUE, nameof(StartAutomatedMapTestTimerWorker) );
|
||
|
}
|
||
|
|
||
|
/** This will look to make certain that all of the streaming levels are finished streaming **/
|
||
|
function StartAutomatedMapTestTimerWorker()
|
||
|
{
|
||
|
local int LevelIdx;
|
||
|
|
||
|
if( WorldInfo != none )
|
||
|
{
|
||
|
for( LevelIdx = 0; LevelIdx < WorldInfo.StreamingLevels.length; ++LevelIdx )
|
||
|
{
|
||
|
if( WorldInfo.StreamingLevels[LevelIdx].bHasLoadRequestPending == TRUE )
|
||
|
{
|
||
|
`log( "levels not streamed in yet sleeping 5s" );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now determine whether or not to check for mem leaks
|
||
|
if( bCheckingForMemLeaks )
|
||
|
{
|
||
|
DoMemoryTracking();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ClearTimer( 'StartAutomatedMapTestTimerWorker' );
|
||
|
SetTimer( 15.0,false,nameof(CloseAutomatedMapTestTimer) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Restart the game when timer pops
|
||
|
*/
|
||
|
function CloseAutomatedMapTestTimer()
|
||
|
{
|
||
|
if( Len(AutomatedMapTestingTransitionMap) > 0)
|
||
|
{
|
||
|
if (AutomatedTestingMapIndex < 0)
|
||
|
{
|
||
|
WorldInfo.Game.RestartGame();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WorldInfo.Game.RestartGame();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function IncrementAutomatedTestingMapIndex()
|
||
|
{
|
||
|
if( bUsingAutomatedTestingMapList == TRUE )
|
||
|
{
|
||
|
if( bAutomatedTestingWithOpen == TRUE )
|
||
|
{
|
||
|
`log( " NumMapListCyclesDone: " $ NumMapListCyclesDone $ " / " $ NumAutomatedMapTestingCycles );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (AutomatedTestingMapIndex >= 0)
|
||
|
{
|
||
|
AutomatedTestingMapIndex++;
|
||
|
}
|
||
|
}
|
||
|
`log( " NextIncrementAutomatedTestingMapIndex: " $ AutomatedTestingMapIndex $ " / " $ AutomatedMapTestingList.Length );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function IncrementNumberOfMatchesPlayed()
|
||
|
{
|
||
|
`log( " Num Matches Played: " $ NumberOfMatchesPlayed );
|
||
|
NumberOfMatchesPlayed++;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** @return the map we should travel to during automated testing */
|
||
|
function string GetNextAutomatedTestingMap()
|
||
|
{
|
||
|
local string MapName;
|
||
|
local PlayerController PC;
|
||
|
local bool bResetMapIndex;
|
||
|
|
||
|
if (bUsingAutomatedTestingMapList)
|
||
|
{
|
||
|
// check to see if we are over the end of the list and then increment num cycles and restart
|
||
|
if ((AutomatedTestingMapIndex >= 0) && (Len(AutomatedMapTestingTransitionMap) > 0))
|
||
|
{
|
||
|
// If the testing map index is >= 0, we are in the transition map
|
||
|
// Increment the map index now... this is to avoid ever trying to set -0
|
||
|
AutomatedTestingMapIndex++;
|
||
|
//
|
||
|
AutomatedTestingMapIndex *= -1;
|
||
|
MapName = AutomatedMapTestingTransitionMap;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Remove the negative if we are using a transition map
|
||
|
if (Len(AutomatedMapTestingTransitionMap) > 0)
|
||
|
{
|
||
|
AutomatedTestingMapIndex *= -1;
|
||
|
}
|
||
|
|
||
|
if (AutomatedTestingMapIndex >= AutomatedMapTestingList.Length)
|
||
|
{
|
||
|
AutomatedTestingMapIndex = 0;
|
||
|
NumMapListCyclesDone++;
|
||
|
bResetMapIndex = true;
|
||
|
}
|
||
|
MapName = AutomatedMapTestingList[AutomatedTestingMapIndex];
|
||
|
}
|
||
|
|
||
|
if (bAutomatedTestingWithOpen == true)
|
||
|
{
|
||
|
// see if we have done all of the cycles we were asked to do
|
||
|
if ((NumMapListCyclesDone >= NumAutomatedMapTestingCycles) && (NumAutomatedMapTestingCycles != 0))
|
||
|
{
|
||
|
if ( bCheckingForMemLeaks )
|
||
|
{
|
||
|
ConsoleCommand( "DEFERRED_STOPMEMTRACKING_AND_DUMP" );
|
||
|
}
|
||
|
|
||
|
if (bExitOnCyclesComplete)
|
||
|
{
|
||
|
ConsoleCommand( "EXIT" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
foreach WorldInfo.AllControllers(class'PlayerController', PC)
|
||
|
{
|
||
|
// check to see if we are over the end of the list and then increment num cycles and restart
|
||
|
if (bResetMapIndex)
|
||
|
{
|
||
|
PC.PlayerReplicationInfo.AutomatedTestingData.NumMapListCyclesDone++;
|
||
|
}
|
||
|
|
||
|
// see if we have done all of the cycles we were asked to do
|
||
|
if ((PC.PlayerReplicationInfo.AutomatedTestingData.NumMapListCyclesDone >= NumAutomatedMapTestingCycles)
|
||
|
&& (NumAutomatedMapTestingCycles != 0)
|
||
|
)
|
||
|
{
|
||
|
if( bCheckingForMemLeaks )
|
||
|
{
|
||
|
ConsoleCommand( "DEFERRED_STOPMEMTRACKING_AND_DUMP" );
|
||
|
}
|
||
|
|
||
|
if (bExitOnCyclesComplete)
|
||
|
{
|
||
|
ConsoleCommand( "EXIT" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
`log("NextAutomatedTestingMap: " $ MapName);
|
||
|
return MapName;
|
||
|
}
|
||
|
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used to initialize automated testing as needed when match starts.
|
||
|
* Called from GameInfo.StartMatch().
|
||
|
*/
|
||
|
function StartMatch()
|
||
|
{
|
||
|
local PlayerController PC;
|
||
|
|
||
|
if ( bAutomatedTestingWithOpen )
|
||
|
{
|
||
|
IncrementNumberOfMatchesPlayed();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
foreach WorldInfo.AllControllers(class'PlayerController', PC)
|
||
|
{
|
||
|
PC.IncrementNumberOfMatchesPlayed();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
IncrementAutomatedTestingMapIndex();
|
||
|
|
||
|
if( bCheckingForFragmentation )
|
||
|
{
|
||
|
ConsoleCommand( "MemFragCheck" );
|
||
|
}
|
||
|
|
||
|
if( AutomatedTestingExecCommandToRunAtStartMatch != "" )
|
||
|
{
|
||
|
`log( "AutomatedTestingExecCommandToRunAtStartMatch: " $ AutomatedTestingExecCommandToRunAtStartMatch );
|
||
|
ConsoleCommand( AutomatedTestingExecCommandToRunAtStartMatch );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start Sentinel Run if needed
|
||
|
* @return true if normal gameinfo startmatch should be aborted
|
||
|
*/
|
||
|
function bool CheckForSentinelRun()
|
||
|
{
|
||
|
if( bDoingASentinelRun )
|
||
|
{
|
||
|
`log( "DoingASentinelRun! task "$SentinelTaskDescription );
|
||
|
|
||
|
// this will take over the normal match rules and do its own thing
|
||
|
if( SentinelTaskDescription ~= "TravelTheWorld" )
|
||
|
{
|
||
|
WorldInfo.Game.DoTravelTheWorld();
|
||
|
return true;
|
||
|
}
|
||
|
// any of these types are going to run in addition to what ever the player is doing
|
||
|
// they just go gather stats based on a timer
|
||
|
else
|
||
|
{
|
||
|
BeginSentinelRun( SentinelTaskDescription, SentinelTaskParameter, SentinelTagDesc );
|
||
|
SetTimer( 3.0f, TRUE, nameof(DoTimeBasedSentinelStatGathering) );
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|