You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
726 lines
21 KiB
726 lines
21 KiB
14 years ago
|
/*==LICENSE==*
|
||
|
|
||
|
CyanWorlds.com Engine - MMOG client, server and tools
|
||
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||
|
or by snail mail at:
|
||
|
Cyan Worlds, Inc.
|
||
|
14617 N Newport Hwy
|
||
|
Mead, WA 99021
|
||
|
|
||
|
*==LICENSE==*/
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// plResManagerHelper - The wonderful helper class that can receive messages
|
||
|
// for the resManager.
|
||
|
//
|
||
|
//// History /////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 6.7.2002 mcn - Created
|
||
|
//
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "hsTypes.h"
|
||
|
#include "plResManagerHelper.h"
|
||
|
#include "plResManager.h"
|
||
|
#include "plRegistryNode.h"
|
||
|
#include "plRegistryHelpers.h"
|
||
|
//#include "plRegistry.h"
|
||
|
#include "plResMgrSettings.h"
|
||
|
|
||
|
#include "../pnKeyedObject/plFixedKey.h"
|
||
|
#include "../plMessage/plResMgrHelperMsg.h"
|
||
|
#include "../plStatusLog/plStatusLog.h"
|
||
|
#include "hsTimer.h"
|
||
|
|
||
|
#ifdef MCN_RESMGR_DEBUGGING
|
||
|
|
||
|
static const int kLogSize = 40;
|
||
|
static const float kUpdateDelay = 0.5f;
|
||
|
|
||
|
#include "../plInputCore/plInputInterface.h"
|
||
|
#include "../plInputCore/plInputDevice.h"
|
||
|
#include "../plInputCore/plInputInterfaceMgr.h"
|
||
|
#include "../pnInputCore/plInputMap.h"
|
||
|
#include "../plMessage/plInputEventMsg.h"
|
||
|
#include "../plMessage/plInputIfaceMgrMsg.h"
|
||
|
#include "../pnKeyedObject/plKeyImp.h"
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/// Logging #define for easier use
|
||
|
#define kResMgrLog( level ) if( plResMgrSettings::Get().GetLoggingLevel() >= level ) plStatusLog::AddLineS( "resources.log",
|
||
|
|
||
|
|
||
|
|
||
|
//// Constructor/Destructor //////////////////////////////////////////////////
|
||
|
|
||
|
plResManagerHelper *plResManagerHelper::fInstance = nil;
|
||
|
|
||
|
plResManagerHelper::plResManagerHelper( plResManager *resMgr )
|
||
|
{
|
||
|
fResManager = resMgr;
|
||
|
fInstance = this;
|
||
|
|
||
|
fInShutdown = false;
|
||
|
#ifdef MCN_RESMGR_DEBUGGING
|
||
|
fDebugScreen = nil;
|
||
|
fCurrAge = -1;
|
||
|
fCurrAgeExpanded = false;
|
||
|
fRefreshing = false;
|
||
|
fDebugDisplayType = 0;
|
||
|
#endif
|
||
|
}
|
||
|
plResManagerHelper::~plResManagerHelper()
|
||
|
{
|
||
|
fInstance = nil;
|
||
|
}
|
||
|
|
||
|
//// Shutdown ////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void plResManagerHelper::Shutdown( void )
|
||
|
{
|
||
|
EnableDebugScreen( false );
|
||
|
UnRegisterAs( kResManagerHelper_KEY );
|
||
|
}
|
||
|
|
||
|
//// Init ////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void plResManagerHelper::Init( void )
|
||
|
{
|
||
|
RegisterAs( kResManagerHelper_KEY );
|
||
|
}
|
||
|
|
||
|
//// MsgReceive //////////////////////////////////////////////////////////////
|
||
|
|
||
|
hsBool plResManagerHelper::MsgReceive( plMessage *msg )
|
||
|
{
|
||
|
plResMgrHelperMsg *refferMsg = plResMgrHelperMsg::ConvertNoRef( msg );
|
||
|
if( refferMsg != nil )
|
||
|
{
|
||
|
if( refferMsg->GetCommand() == plResMgrHelperMsg::kKeyRefList )
|
||
|
{
|
||
|
// Message to let go of these keys. So unref the key list, destroy it and we're done!
|
||
|
kResMgrLog( 2 ) 0xff80ff80, "Dropping page keys after timed delay" );
|
||
|
hsStatusMessage( "*** Dropping page keys after timed delay ***" );
|
||
|
|
||
|
delete refferMsg->fKeyList;
|
||
|
refferMsg->fKeyList = nil;
|
||
|
}
|
||
|
else if( refferMsg->GetCommand() == plResMgrHelperMsg::kUpdateDebugScreen )
|
||
|
{
|
||
|
IUpdateDebugScreen();
|
||
|
}
|
||
|
else if( refferMsg->GetCommand() == plResMgrHelperMsg::kEnableDebugScreen )
|
||
|
EnableDebugScreen( true );
|
||
|
else if( refferMsg->GetCommand() == plResMgrHelperMsg::kDisableDebugScreen )
|
||
|
EnableDebugScreen( false );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return hsKeyedObject::MsgReceive( msg );
|
||
|
}
|
||
|
|
||
|
//// Read/Write //////////////////////////////////////////////////////////////
|
||
|
|
||
|
void plResManagerHelper::Read( hsStream *s, hsResMgr *mgr )
|
||
|
{
|
||
|
hsAssert( false, "You should never read me in!" );
|
||
|
}
|
||
|
|
||
|
void plResManagerHelper::Write( hsStream *s, hsResMgr *mgr )
|
||
|
{
|
||
|
hsAssert( false, "You should never write me out!" );
|
||
|
}
|
||
|
|
||
|
//// LoadAndHoldPageKeys /////////////////////////////////////////////////////
|
||
|
// Loads and refs the keys for the given page, then sends the ref list as
|
||
|
// a list to ourself, time delayed 1 second, so that we can unref them one
|
||
|
// second later.
|
||
|
|
||
|
void plResManagerHelper::LoadAndHoldPageKeys( plRegistryPageNode *page )
|
||
|
{
|
||
|
hsAssert( GetKey() != nil, "Can't load and hold keys when we don't have a key for the helper" );
|
||
|
|
||
|
// Create our msg
|
||
|
plResMgrHelperMsg *refferMsg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kKeyRefList );
|
||
|
refferMsg->fKeyList = TRACKED_NEW plResPageKeyRefList;
|
||
|
|
||
|
fResManager->LoadPageKeys(page);
|
||
|
page->IterateKeys( refferMsg->fKeyList );
|
||
|
|
||
|
// Load and ref the keys
|
||
|
#ifdef HS_DEBUGGING
|
||
|
char msg[ 256 ];
|
||
|
sprintf( msg, "*** Temporarily loading keys for room %s>%s based on FindKey() query, will drop in 1 sec ***", page->GetPageInfo().GetAge(), page->GetPageInfo().GetPage() );
|
||
|
hsStatusMessage( msg );
|
||
|
#endif
|
||
|
kResMgrLog( 2 ) 0xff80ff80, "Temporarily loading keys for room %s>%s, will drop in 1 sec", page->GetPageInfo().GetAge(), page->GetPageInfo().GetPage() );
|
||
|
|
||
|
// Deliver the message to ourselves!
|
||
|
refferMsg->SetTimeStamp( hsTimer::GetSysSeconds() + 1.f );
|
||
|
refferMsg->Send( GetKey() );
|
||
|
}
|
||
|
|
||
|
#ifdef MCN_RESMGR_DEBUGGING
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//// plResMgrDebugInterface Definition ///////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
class plResMgrDebugInterface : public plInputInterface
|
||
|
{
|
||
|
protected:
|
||
|
plResManagerHelper * const fParent;
|
||
|
|
||
|
virtual ControlEventCode *IGetOwnedCodeList( void ) const
|
||
|
{
|
||
|
static ControlEventCode codes[] = { END_CONTROLS };
|
||
|
return codes;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
plResMgrDebugInterface( plResManagerHelper * const mgr ) : fParent( mgr ) { SetEnabled( true ); }
|
||
|
|
||
|
virtual UInt32 GetPriorityLevel( void ) const { return kGUISystemPriority + 10; }
|
||
|
virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg )
|
||
|
{
|
||
|
plKeyEventMsg *pKeyMsg = plKeyEventMsg::ConvertNoRef( pMsg );
|
||
|
if( pKeyMsg != nil && pKeyMsg->GetKeyDown() )
|
||
|
{
|
||
|
if( pKeyMsg->GetKeyCode() == KEY_UP && fParent->fCurrAge >= 0 )
|
||
|
{
|
||
|
fParent->fCurrAge--;
|
||
|
fParent->IUpdateDebugScreen( true );
|
||
|
return true;
|
||
|
}
|
||
|
else if( pKeyMsg->GetKeyCode() == KEY_DOWN )
|
||
|
{
|
||
|
fParent->fCurrAge++;
|
||
|
fParent->IUpdateDebugScreen( true );
|
||
|
return true;
|
||
|
}
|
||
|
else if( pKeyMsg->GetKeyCode() == KEY_ENTER )
|
||
|
{
|
||
|
fParent->fCurrAgeExpanded = !fParent->fCurrAgeExpanded;
|
||
|
fParent->IUpdateDebugScreen( true );
|
||
|
return true;
|
||
|
}
|
||
|
else if( pKeyMsg->GetKeyCode() == KEY_ESCAPE )
|
||
|
{
|
||
|
plResMgrHelperMsg *msg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kDisableDebugScreen );
|
||
|
msg->Send( fParent->GetKey() );
|
||
|
return true;
|
||
|
}
|
||
|
else if( pKeyMsg->GetKeyCode() == KEY_RIGHT )
|
||
|
{
|
||
|
if( !fParent->fCurrAgeExpanded )
|
||
|
fParent->fCurrAgeExpanded = true;
|
||
|
else
|
||
|
{
|
||
|
fParent->fDebugDisplayType++;
|
||
|
if( fParent->fDebugDisplayType == plResManagerHelper::kMaxDisplayType )
|
||
|
fParent->fDebugDisplayType = 0;
|
||
|
}
|
||
|
fParent->IUpdateDebugScreen( true );
|
||
|
return true;
|
||
|
}
|
||
|
else if( pKeyMsg->GetKeyCode() == KEY_LEFT )
|
||
|
{
|
||
|
fParent->fCurrAgeExpanded = false;
|
||
|
fParent->IUpdateDebugScreen( true );
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
virtual UInt32 GetCurrentCursorID( void ) const { return 0; }
|
||
|
virtual hsBool HasInterestingCursorID( void ) const { return false; }
|
||
|
};
|
||
|
|
||
|
#endif
|
||
|
|
||
|
//// EnableDebugScreen ///////////////////////////////////////////////////////
|
||
|
|
||
|
void plResManagerHelper::EnableDebugScreen( hsBool enable )
|
||
|
{
|
||
|
#ifdef MCN_RESMGR_DEBUGGING
|
||
|
if( enable )
|
||
|
{
|
||
|
if( fDebugScreen == nil )
|
||
|
{
|
||
|
fDebugScreen = plStatusLogMgr::GetInstance().CreateStatusLog( kLogSize, "ResManager Status", plStatusLog::kFilledBackground | plStatusLog::kDontWriteFile );
|
||
|
fRefreshing = true;
|
||
|
|
||
|
plResMgrHelperMsg *msg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kUpdateDebugScreen );
|
||
|
// msg->SetTimeStamp( hsTimer::GetSysSeconds() + kUpdateDelay );
|
||
|
msg->Send( GetKey() );
|
||
|
|
||
|
fDebugInput = TRACKED_NEW plResMgrDebugInterface( this );
|
||
|
plInputIfaceMgrMsg *imsg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kAddInterface );
|
||
|
imsg->SetIFace( fDebugInput );
|
||
|
imsg->Send();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fRefreshing = false;
|
||
|
if( fDebugScreen != nil )
|
||
|
{
|
||
|
delete fDebugScreen;
|
||
|
fDebugScreen = nil;
|
||
|
|
||
|
plInputIfaceMgrMsg *imsg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface );
|
||
|
imsg->SetIFace( fDebugInput );
|
||
|
imsg->Send();
|
||
|
|
||
|
hsRefCnt_SafeUnRef( fDebugInput );
|
||
|
fDebugInput = nil;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//// IUpdateDebugScreen /////////////////////////////////////////////////////
|
||
|
|
||
|
#ifdef MCN_RESMGR_DEBUGGING
|
||
|
class plDebugPrintIterator : public plRegistryPageIterator, plRegistryKeyIterator
|
||
|
{
|
||
|
public:
|
||
|
plStatusLog *fLog;
|
||
|
UInt8 fStep, fLines;
|
||
|
UInt32 &fLoadedCount, &fHoldingCount, fPageCount, fAgeIndex;
|
||
|
char fCurrAge[ 128 ];
|
||
|
UInt32 fLoadedKeys, fTotalKeys, fTotalSize, fLoadedSize;
|
||
|
|
||
|
plResManagerHelper *fParent;
|
||
|
|
||
|
plDebugPrintIterator( plResManagerHelper *parent, plStatusLog *log, UInt32 &loadedCount, UInt32 &holdingCount )
|
||
|
: fParent( parent ), fLog( log ), fStep( 0 ), fLines( 0 ), fLoadedCount( loadedCount ), fHoldingCount( holdingCount )
|
||
|
{
|
||
|
fLoadedCount = fHoldingCount = 0;
|
||
|
fCurrAge[ 0 ] = 0;
|
||
|
fPageCount = 0;
|
||
|
fAgeIndex = 0;
|
||
|
}
|
||
|
|
||
|
virtual hsBool EatPage( plRegistryPageNode *page )
|
||
|
{
|
||
|
if( fStep == 0 )
|
||
|
{
|
||
|
fLog->AddLineF( 0xff80ff80, "Loaded Pages" );
|
||
|
fStep = 1;
|
||
|
fLines++;
|
||
|
}
|
||
|
else if( fStep == 1 && page != nil && !page->IsLoaded() )
|
||
|
{
|
||
|
fStep = 2;
|
||
|
fLog->AddLineF( 0xff80ff80, "Holding Pages" );
|
||
|
fLines++;
|
||
|
}
|
||
|
|
||
|
if( page != nil && page->IsLoaded() )
|
||
|
fLoadedCount++;
|
||
|
else if( page != nil )
|
||
|
fHoldingCount++;
|
||
|
|
||
|
// Changed ages?
|
||
|
if( page == nil || strcmp( fCurrAge, page->GetPageInfo().GetAge() ) != 0 )
|
||
|
{
|
||
|
// Print some info for the last age we were on
|
||
|
if( fCurrAge[ 0 ] != 0 )
|
||
|
{
|
||
|
if( fParent->fCurrAge != fAgeIndex || !fParent->fCurrAgeExpanded )
|
||
|
{
|
||
|
if( fLines < kLogSize - 4 )
|
||
|
{
|
||
|
UInt32 color = plStatusLog::kWhite;
|
||
|
if( fParent->fCurrAge == fAgeIndex )
|
||
|
color = plStatusLog::kYellow;
|
||
|
|
||
|
fLog->AddLineF( color, " %s (%d pages)", fCurrAge, fPageCount );
|
||
|
fLines++;
|
||
|
}
|
||
|
else if( fLines == kLogSize - 4 )
|
||
|
{
|
||
|
fLog->AddLineF( plStatusLog::kWhite, " ..." );
|
||
|
fLines++;
|
||
|
}
|
||
|
}
|
||
|
fAgeIndex++;
|
||
|
}
|
||
|
fPageCount = 0;
|
||
|
if( page != nil )
|
||
|
strncpy( fCurrAge, page->GetPageInfo().GetAge(), sizeof( fCurrAge ) - 1 );
|
||
|
else
|
||
|
fCurrAge[ 0 ] = 0;
|
||
|
|
||
|
if( fParent->fCurrAge == fAgeIndex && fParent->fCurrAgeExpanded )
|
||
|
{
|
||
|
// Print header now, since we won't be printing a footer
|
||
|
if( fLines < kLogSize - 4 )
|
||
|
{
|
||
|
fLog->AddLineF( plStatusLog::kYellow, " %s>", fCurrAge );
|
||
|
fLines++;
|
||
|
}
|
||
|
else if( fLines == kLogSize - 4 )
|
||
|
{
|
||
|
fLog->AddLineF( plStatusLog::kWhite, " ..." );
|
||
|
fLines++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fPageCount++;
|
||
|
|
||
|
if( fParent->fCurrAge == fAgeIndex && fParent->fCurrAgeExpanded && page != nil )
|
||
|
{
|
||
|
// Count keys for this page
|
||
|
fTotalKeys = fLoadedKeys = fTotalSize = fLoadedSize = 0;
|
||
|
page->IterateKeys( this );
|
||
|
|
||
|
// Print page for this expanded age view
|
||
|
if( fLines < kLogSize - 4 )
|
||
|
{
|
||
|
if( fParent->fDebugDisplayType == plResManagerHelper::kSizes )
|
||
|
fLog->AddLineF( plStatusLog::kWhite, " %s (%d keys @ %4.1fk, %d loaded @ %4.1fk)", page->GetPageInfo().GetPage(), fTotalKeys, fTotalSize / 1024.f, fLoadedKeys, fLoadedSize / 1024.f );
|
||
|
else if( fParent->fDebugDisplayType == plResManagerHelper::kPercents )
|
||
|
fLog->AddLineF( plStatusLog::kWhite, " %s (%d%% loaded of %d keys @ %4.1fk)", page->GetPageInfo().GetPage(), fLoadedSize * 100 / ( fTotalSize > 0 ? fTotalSize : -1 ), fTotalKeys, fTotalSize / 1024.f );
|
||
|
else //if( fParent->fDebugDisplayType == plResManagerHelper::kBars )
|
||
|
{
|
||
|
const int startPos = 20, length = 32;
|
||
|
|
||
|
char line[ 128 ];
|
||
|
memset( line, ' ', sizeof( line ) - 1 );
|
||
|
line[ 127 ] = 0;
|
||
|
if( strlen( page->GetPageInfo().GetPage() ) < startPos - 2 )
|
||
|
memcpy( line + 2, page->GetPageInfo().GetPage(), strlen( page->GetPageInfo().GetPage() ) );
|
||
|
else
|
||
|
memcpy( line + 2, page->GetPageInfo().GetPage(), startPos - 2 );
|
||
|
|
||
|
line[ startPos ] = '|';
|
||
|
if( fTotalSize == 0 )
|
||
|
{
|
||
|
line[ startPos + 1 ] = '|';
|
||
|
line[ startPos + 2 ] = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
char temp[ 12 ];
|
||
|
sprintf( temp, "%d%%", fLoadedSize * 100 / fTotalSize );
|
||
|
|
||
|
line[ startPos + length + 1 ] = '|';
|
||
|
int i, sum = 0;
|
||
|
for( i = startPos + 1; i < startPos + length + 1 && sum < fLoadedSize; i++ )
|
||
|
{
|
||
|
line[ i ] = '=';
|
||
|
sum += fTotalSize / length;
|
||
|
}
|
||
|
line[ startPos + length + 2 ] = 0;
|
||
|
|
||
|
memcpy( line + startPos + 1, temp, strlen( temp ) );
|
||
|
}
|
||
|
|
||
|
fLog->AddLine( line, plStatusLog::kWhite );
|
||
|
}
|
||
|
fLines++;
|
||
|
}
|
||
|
else if( fLines == kLogSize - 4 )
|
||
|
{
|
||
|
fLog->AddLineF( plStatusLog::kWhite, " ..." );
|
||
|
fLines++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
virtual hsBool EatKey( const plKey& key )
|
||
|
{
|
||
|
if( key->ObjectIsLoaded() )
|
||
|
{
|
||
|
fLoadedKeys++;
|
||
|
fLoadedSize += ((plKeyImp *)key)->GetDataLen();
|
||
|
}
|
||
|
fTotalKeys++;
|
||
|
fTotalSize += ((plKeyImp *)key)->GetDataLen();
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
void plResManagerHelper::IUpdateDebugScreen( hsBool force )
|
||
|
{
|
||
|
#ifdef MCN_RESMGR_DEBUGGING
|
||
|
|
||
|
if( !fRefreshing )
|
||
|
return;
|
||
|
|
||
|
plRegistry *reg = fResManager->IGetRegistry();
|
||
|
UInt32 loadedCnt, holdingCnt;
|
||
|
|
||
|
fDebugScreen->Clear();
|
||
|
|
||
|
plDebugPrintIterator iter( this, fDebugScreen, loadedCnt, holdingCnt );
|
||
|
reg->IterateAllPages( &iter );
|
||
|
iter.EatPage( nil ); // Force a final update
|
||
|
|
||
|
fDebugScreen->AddLineF( plStatusLog::kGreen, "%d pages loaded, %d holding", loadedCnt, holdingCnt );
|
||
|
|
||
|
if( fCurrAge >= iter.fAgeIndex )
|
||
|
fCurrAge = -1;
|
||
|
|
||
|
// Repump our update
|
||
|
if( !force )
|
||
|
{
|
||
|
plResMgrHelperMsg *msg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kUpdateDebugScreen );
|
||
|
msg->SetTimeStamp( hsTimer::GetSysSeconds() + kUpdateDelay );
|
||
|
msg->Send( GetKey() );
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
// FIXME
|
||
|
hsBool VerifyKeyUnloaded(const char* logFile, const plKey& key);
|
||
|
// Verifies that a key which shouldn't be loaded isn't, and if it is tries to figure out why.
|
||
|
void VerifyAgeUnloaded(const char* logFile, const char* age);
|
||
|
|
||
|
// Helper for VerifyKeyUnloaded
|
||
|
hsBool IVerifyKeyUnloadedRecur(const char* logFile, const plKey& baseKey, const plKey& upKey, const char* baseAge);
|
||
|
bool ILookForCyclesRecur(const char* logFile, const plKey& key, hsTArray<plKey>& tree, int& cycleStart);
|
||
|
|
||
|
bool plResManager::ILookForCyclesRecur(const char* logFile, const plKey& key, hsTArray<plKey>& tree, int& cycleStart)
|
||
|
{
|
||
|
int idx = tree.Find(key);
|
||
|
tree.Append(key);
|
||
|
if (tree.kMissingIndex != idx)
|
||
|
{
|
||
|
cycleStart = idx;
|
||
|
// Found a cycle.
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Now recurse up the active reference tree.
|
||
|
for (int i = 0; i < key->GetNumNotifyCreated(); i++)
|
||
|
{
|
||
|
if (key->GetActiveBits().IsBitSet(i))
|
||
|
{
|
||
|
for (int j = 0; j < key->GetNotifyCreated(i)->GetNumReceivers(); j++)
|
||
|
{
|
||
|
plKey reffer = key->GetNotifyCreated(i)->GetReceiver(j);
|
||
|
|
||
|
if (ILookForCyclesRecur(logFile, reffer, tree, cycleStart))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
tree.Pop();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool plResManager::IVerifyKeyUnloadedRecur(const char* logFile, const plKey& baseKey, const plKey& upKey, const char* baseAge)
|
||
|
{
|
||
|
const plPageInfo& pageInfo = FindPage(upKey->GetUoid().GetLocation())->GetPageInfo();
|
||
|
const char* upAge = pageInfo.GetAge();
|
||
|
const char* upPage = pageInfo.GetPage();
|
||
|
|
||
|
if( !upKey->GetActiveRefs() )
|
||
|
{
|
||
|
// We've hit a key active reffing us that should be inactive.
|
||
|
// If it's object is loaded, then it somehow missed getting unloaded.
|
||
|
// Else it must have missed letting go of us when it got unloaded.
|
||
|
if( upKey->ObjectIsLoaded() )
|
||
|
{
|
||
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which is loaded but nothing is reffing",
|
||
|
upKey->GetName(),
|
||
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()),
|
||
|
upPage);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which isn't even loaded",
|
||
|
upKey->GetName(),
|
||
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()),
|
||
|
upPage);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if the age of this key is different from the age on the baseKey,
|
||
|
// we've got a cross age active ref, which is illegal.
|
||
|
if( stricmp(upAge, baseAge) )
|
||
|
{
|
||
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] which is in a different age %s-%s",
|
||
|
upKey->GetName(),
|
||
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()),
|
||
|
upAge,
|
||
|
upPage);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int numActive = 0;
|
||
|
int i;
|
||
|
for( i = 0; i < upKey->GetNumNotifyCreated(); i++ )
|
||
|
{
|
||
|
if( upKey->GetActiveBits().IsBitSet(i) )
|
||
|
{
|
||
|
numActive++;
|
||
|
}
|
||
|
}
|
||
|
if( numActive < upKey->GetActiveRefs() )
|
||
|
{
|
||
|
// Someone has AddRef'd us
|
||
|
plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which is loaded due to %d AddRef(s)",
|
||
|
upKey->GetName(),
|
||
|
plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()),
|
||
|
upPage,
|
||
|
upKey->GetActiveRefs()-numActive);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Now recurse up the active reference tree.
|
||
|
for( i = 0; i < upKey->GetNumNotifyCreated(); i++ )
|
||
|
{
|
||
|
if( upKey->GetActiveBits().IsBitSet(i) )
|
||
|
{
|
||
|
int j;
|
||
|
for( j = 0; j < upKey->GetNotifyCreated(i)->GetNumReceivers(); j++ )
|
||
|
{
|
||
|
plKey reffer = upKey->GetNotifyCreated(i)->GetReceiver(j);
|
||
|
|
||
|
|
||
|
if( IVerifyKeyUnloadedRecur(logFile, baseKey, reffer, baseAge) )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
hsBool plResManager::VerifyKeyUnloaded(const char* logFile, const plKey& key)
|
||
|
{
|
||
|
if( key->ObjectIsLoaded() )
|
||
|
{
|
||
|
const plPageInfo& pageInfo = FindPage(key->GetUoid().GetLocation())->GetPageInfo();
|
||
|
const char* age = pageInfo.GetAge();
|
||
|
const char* page = pageInfo.GetPage();
|
||
|
|
||
|
plStatusLog::AddLineS(logFile, "==================================");
|
||
|
plStatusLog::AddLineS(logFile, "Object %s [%s] page %s is loaded", key->GetName(), plFactory::GetNameOfClass(key->GetUoid().GetClassType()), page);
|
||
|
|
||
|
hsTArray<plKey> tree;
|
||
|
int cycleStart;
|
||
|
hsBool hasCycle = ILookForCyclesRecur(logFile, key, tree, cycleStart);
|
||
|
if( hasCycle )
|
||
|
{
|
||
|
plStatusLog::AddLineS(logFile, "\t%s [%s] held by dependency cycle", key->GetName(), plFactory::GetNameOfClass(key->GetUoid().GetClassType()));
|
||
|
int i;
|
||
|
for( i = cycleStart; i < tree.GetCount(); i++ )
|
||
|
{
|
||
|
plStatusLog::AddLineS(logFile, "\t%s [%s]", tree[i]->GetName(), plFactory::GetNameOfClass(tree[i]->GetUoid().GetClassType()));
|
||
|
}
|
||
|
plStatusLog::AddLineS(logFile, "\tEnd Cycle");
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IVerifyKeyUnloadedRecur(logFile, key, key, age);
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
class plValidateKeyIterator : public plRegistryKeyIterator
|
||
|
{
|
||
|
protected:
|
||
|
plRegistry* fRegistry;
|
||
|
const char* fLogFile;
|
||
|
|
||
|
public:
|
||
|
plValidateKeyIterator(const char* logFile, plRegistry* reg)
|
||
|
{
|
||
|
fRegistry = reg;
|
||
|
fLogFile = logFile;
|
||
|
}
|
||
|
virtual hsBool EatKey(const plKey& key)
|
||
|
{
|
||
|
fRegistry->VerifyKeyUnloaded(fLogFile, key);
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class plValidatePageIterator : public plRegistryPageIterator
|
||
|
{
|
||
|
protected:
|
||
|
const char* fAge;
|
||
|
plRegistryKeyIterator* fIter;
|
||
|
|
||
|
public:
|
||
|
plValidatePageIterator(const char* age, plRegistryKeyIterator* iter) : fAge(age), fIter(iter) {}
|
||
|
|
||
|
|
||
|
virtual hsBool EatPage( plRegistryPageNode *keyNode )
|
||
|
{
|
||
|
if( !stricmp(fAge, keyNode->GetPageInfo().GetAge()) )
|
||
|
return keyNode->IterateKeys( fIter );
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void plResManager::VerifyAgeUnloaded(const char* logFile, const char* age)
|
||
|
{
|
||
|
hsBool autoLog = false;
|
||
|
char buff[256];
|
||
|
if( !logFile || !*logFile )
|
||
|
{
|
||
|
sprintf(buff, "%s.log", age);
|
||
|
logFile = buff;
|
||
|
autoLog = true;
|
||
|
}
|
||
|
|
||
|
if( !autoLog )
|
||
|
{
|
||
|
plStatusLog::AddLineS(logFile, "///////////////////////////////////");
|
||
|
plStatusLog::AddLineS(logFile, "Begin Verification of age %s", age);
|
||
|
}
|
||
|
|
||
|
plValidateKeyIterator keyIter(logFile, this);
|
||
|
plValidatePageIterator pageIter(age, &keyIter);
|
||
|
|
||
|
IterateAllPages(&pageIter);
|
||
|
|
||
|
if( !autoLog )
|
||
|
{
|
||
|
plStatusLog::AddLineS(logFile, "End Verification of age %s", age);
|
||
|
plStatusLog::AddLineS(logFile, "///////////////////////////////////");
|
||
|
}
|
||
|
}
|
||
|
#endif
|